这里需要记忆围栏吗?

时间:2015-07-24 13:11:27

标签: c# .net multithreading memory-fences

基本上我有以下情况:

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在两个线程中都不是null;我也不关心'this.Obj'的同步。我关心的是,一旦我阅读了引用tmp = Obj,内容(例如AB)也是有效的。

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

似乎人们不喜欢这个问题。

我的问题来自以下内容。我已经读过内存栅栏,他们保证:(引用)

  

执行当前线程的处理器无法重新排序指令,使得在调用MemoryBarrier之前的内存访问在对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之前运行对象初始值设定项?或者我是否需要使用内存栅栏来手动确保?

1 个答案:

答案 0 :(得分:1)

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

  1. 制作新的MyObject
  2. i分配给成员A
  3. 2分配给成员B
  4. 将新对象分配到this.Obj
  5. 因此,步骤3和4之间似乎需要屏障。另一种选择是制作this.Obj volatile。这将确保在写入this.Obj之后不允许移动其他读取或写入,同时强制执行所需的排序。