从我关于SO的另一个问题我发现它可能遵循简单的方法
void B()
{
if (_complete)
{
Console.WriteLine (_answer);
}
}
Run Code Online (Sandbox Code Playgroud)
如果在if和console writeline之间发生上下文切换,可能会在不同的CPU上执行..这对我来说是新闻,所以我想知道现在什么时候单线程代码可以转换为另一个CPU以及为什么它可能在上面这么简单的情况下有意义?
由于其 TSO 内存模型,X86 保证所有商店的总顺序。我的问题是是否有人知道这是如何实际实施的。
我对所有 4 个围栏是如何实现的印象很好,所以我可以解释如何保留本地秩序。但是 4 个栅栏只会给 PO;它不会给您 TSO(我知道 TSO 允许旧商店跳到新负载前面,因此只需要 4 个围栏中的 3 个)。
单个地址上所有内存操作的总顺序是一致性的责任。但我想知道英特尔(特别是 Skylake)如何在多个地址的商店上实现总订单。
x86 intel cpu-architecture memory-barriers micro-architecture
假设我的代码如下:
struct Foo {
Foo() : x(10) {}
int x_;
}
void WillRunInThread(const Foo* f) {
cout << "f.x_ is: " << f->x_ << endl;
}
struct Baz {
Baz() : foo_(), t_(&WillRunInThread, &foo_) {}
Foo foo_;
std::thread t_;
}
int main(int argc, char** argv) {
Baz b;
b.t_.join();
}
Run Code Online (Sandbox Code Playgroud)
我保证WillRunInThread会打印 10 个吗?我已经看到一些 StackOverflow 答案表明这是安全的。但是,我不确定情况是否如此。具体来说,我认为存在以下两种可能性,这两种可能性都会产生问题:
Foo.x_可以在t_启动时存储在寄存器中。foo_将出现前被修建t_,这只是在同一个线程。例如,编译器可以自由地重新排序foo_.x_until after的初始化t_。我没有看到std::thread的构造函数作为任何类型的内存栅栏来防止上述两个问题的迹象。但是,我经常看到像上面这样的代码,这让我觉得我错过了一些东西。任何澄清将不胜感激。
我想知道为什么需要内存屏障,我已经阅读了一些关于这个主题的文章。
有人说这是因为cpu乱序执行,而另一些人说这是因为缓存一致性问题,存储缓冲区和无效队列导致。
那么,需要内存屏障的真正原因是什么?cpu乱序执行或者缓存一致性问题?或两者?cpu乱序执行和缓存一致性有关系吗?x86和arm有什么区别?
这是一个后续问题
有很多文章和博客提到 Java 和 JVM 指令重新排序,这可能会导致用户操作中出现反直觉的结果。
当我要求演示 Java 指令重新排序导致意外结果时,有几条评论说,更普遍的关注领域是内存重新排序,并且很难在 x86 CPU 上进行演示。
指令重新排序只是内存重新排序、编译器优化和内存模型等更大问题的一部分吗?这些问题真的是 Java 编译器和 JVM 特有的吗?它们是否特定于某些 CPU 类型?
java cpu-architecture memory-barriers instruction-reordering
根据 cppreference,一个volatile限定变量的存储不能重新排序为另一个volatile限定变量。换句话说,在下面的示例中,当 y 变为 20 时,可以保证 x 为 10。
volatile int x, y;
...
x = 10;
y = 20;
Run Code Online (Sandbox Code Playgroud)
根据维基百科,ARM 处理器的一个存储可以在另一个存储之后重新排序。因此,在下面的示例中,第二个存储可以在第一个存储之前执行,因为两个目的地是不相交的,因此它们可以自由地重新排序。
str r1, [r3]
str r2, [r3, #4]
Run Code Online (Sandbox Code Playgroud)
有了这样的理解,我写了一个玩具程序:
volatile int x, y;
int main() {
x = 10;
y = 20;
}
Run Code Online (Sandbox Code Playgroud)
我预计生成的程序集中会出现一些围栏,以保证 x 和 y 的存储顺序。但为 ARM生成的程序集是:
main:
movw r3, #:lower16:.LANCHOR0
movt r3, #:upper16:.LANCHOR0
movs r1, #10
movs r2, #20
movs r0, #0
str r1, [r3]
str r2, [r3, #4] …Run Code Online (Sandbox Code Playgroud) 我试图了解内存障碍并且遇到了下面的维基百科链接 http://en.wikipedia.org/wiki/Memory_barrier 这很好地解释了这个概念,但有想法,如果这对我们有mutex()锁定的系统真有帮助记忆部分.
使用维基百科中提到的相同代码,以下方法是否会使用互斥锁解决问题?
[注意:函数名称并非特定于任何编程语言,仅用于简单起见]
处理器#1
mutex_lock(a)
while (f == 0);
print x;
mutex_unlock(a)
Run Code Online (Sandbox Code Playgroud)
处理器#2
mutex_lock(a)
x = 42;
f = 1;
mutex_unlock(a)
Run Code Online (Sandbox Code Playgroud) 鉴于 x86 具有强大的内存模型,是否有必要使用std::memory_order_acquirefence(不是 operation)?
例如,如果我有这个代码:
uint32_t read_shm(const uint64_t offset) {
// m_head_memory_location is char* pointing to the beginning of a mmap-ed named shared memory segment
// a different process on different core will write to it.
return *(uint32_t*)(m_head_memory_location + offset);
}
....
int main() {
uint32_t val = 0;
while (0 != (val = shm.read(some location)));
.... // use val
}
Run Code Online (Sandbox Code Playgroud)
我真的需要std::atomic_thread_fence(std::memory_order_acquire)在return语句之前吗?
我觉得没有必要,因为上面代码的目标是尝试从 读取前 4 个字节m_head_memory_location + offset,因此重新排序栅栏后的任何内存操作都不会影响结果。
或者有一些副作用使获取栅栏变得必要?
在 x86 上是否需要获取栅栏(不是操作)? …
是否有可能通过内存屏障实现易失性变量的相同“保证”(始终读/写内存而不是寄存器)?只需在一个线程中写入变量,然后在另一个线程中读取其值。下面的内容是等价的吗?
#define rmb() __sync_synchronize()
#define wmb() __sync_synchronize()
static volatile int a;
static int b;
//invoked by thread 1
static void writer(void)
{
...
a = 1;
...
b = 1;
wmb();
}
//invoked by thread 2
static void reader(void)
{
...
while (a != 1)
;
// do something
...
while (b != 1)
rmb();
// do something
}
Run Code Online (Sandbox Code Playgroud)
编辑:好的,我知道 volatile 不能保证原子性、可见性或顺序。内存屏障除了排序之外还提供其他功能吗?还有能见度?除了 _Atomic C11 或 gcc/clang 原子内置函数之外,还有其他东西可以保证可见性吗?
在 Java 应用程序中,如果对对象状态的访问发生在同一线程上(在最简单的情况下,在单线程应用程序中),则无需同步以强制更改的可见性/一致性,如发生在关系规范:
“两个动作可以通过先发生关系排序。如果一个动作先发生在另一个动作之前,那么第一个动作对第二个动作可见并在第二个动作之前排序。
如果我们有两个动作 x 和 y,我们写 hb(x, y) 来表示 x 发生在 y 之前。
如果 x 和 y 是同一线程的动作,并且 x 在程序顺序中排在 y 之前,则 hb(x, y)。”
但是现代架构是多核的,所以 Java 线程可以在任何给定时间在其中任何一个上执行(除非这不是真的并且 Java 线程被固定到特定的内核?)。因此,如果是这种情况,如果一个线程写入变量 x,将其缓存在 L1 缓存或 CPU 寄存器中,然后开始在另一个内核上运行,该内核先前访问过 x 并将其缓存在寄存器中,则该值是不一致的.. . 当线程从 CPU 上取下时是否有某种机制(隐式内存屏障)?
java concurrency synchronization memory-model memory-barriers