这里需要内存栅栏吗?

Is a memory fence required here?

基本上我有以下几种情况:

var tmp1 = new MyObject() { A = i, B = 2 };
// ** write barrier here??
this.Obj = tmp1;

另一个线程可以做这样的事情:

var tmp = this.Obj;
// ** read barrier here?? 
use(tmp.A);

像'Obj'这样的对象只写入一次,然后被多个线程(多次)读取。

我知道 Obj 在两个线程中都不会为空;我也不关心 'this.Obj' 的同步。我关心的是,一旦我阅读了参考文献 tmp = Obj,内容(例如 AB)也是有效的。

我的问题是:我是否需要在上面标记的位置设置内存屏障(例如Thread.MemoryBarrier();)来确保或者这是否总是隐含的?


看来大家不喜欢这个问题。

我的问题来源于以下。我已经阅读了内存栅栏,他们保证:(引用)

The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier execute after memory accesses that follow the call to MemoryBarrier.

如果您查看代码,CPU / 编译器可能能够重写代码:

var tmp1 = new MyObject();
tmp1.A = i;
tmp1.B = 2;
this.Obj = tmp1;

更糟:

var tmp1 = new MyObject();
this.Obj = tmp1;
tmp1.A = i;
tmp1.B = 2;

如果另一个线程拾取最后一个案例,它可以从内存中读取 this.Obj,而 AB 仍然具有默认值。

请注意,这不仅仅是编译器能够重新排序的问题;这也是允许 CPU 重新排序的问题。

换句话说:(感谢@MattBurland)

在tmp1赋值给this.Obj之前,是否保证对象初始化器是运行?还是我需要使用内存栅栏来手动确保这一点?

C# 规范仅保证重新排序不会影响当前线程看到的内容。因此,JIT 似乎可以自由地重新排序下面的操作 2-4,因为它不会影响生产者线程的行为:

  1. 新建一个MyObject
  2. 分配 i 给成员 A
  3. 分配 2 给成员 B
  4. 将新对象分配给this.Obj

因此,似乎第 3 步和第 4 步之间需要一个障碍。另一种选择是 this.Obj volatile。这将确保在写入 this.Obj 之后不允许移动其他读取或写入,同时强制执行所需的顺序。