这里需要内存栅栏吗?
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
,内容(例如 A
和 B
)也是有效的。
我的问题是:我是否需要在上面标记的位置设置内存屏障(例如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
,而 A
和 B
仍然具有默认值。
请注意,这不仅仅是编译器能够重新排序的问题;这也是允许 CPU 重新排序的问题。
换句话说:(感谢@MattBurland)
在tmp1赋值给this.Obj
之前,是否保证对象初始化器是运行?还是我需要使用内存栅栏来手动确保这一点?
C# 规范仅保证重新排序不会影响当前线程看到的内容。因此,JIT 似乎可以自由地重新排序下面的操作 2-4,因为它不会影响生产者线程的行为:
- 新建一个
MyObject
- 分配
i
给成员 A
- 分配
2
给成员 B
- 将新对象分配给
this.Obj
因此,似乎第 3 步和第 4 步之间需要一个障碍。另一种选择是 this.Obj
volatile
。这将确保在写入 this.Obj
之后不允许移动其他读取或写入,同时强制执行所需的顺序。
基本上我有以下几种情况:
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
,内容(例如 A
和 B
)也是有效的。
我的问题是:我是否需要在上面标记的位置设置内存屏障(例如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
,而 A
和 B
仍然具有默认值。
请注意,这不仅仅是编译器能够重新排序的问题;这也是允许 CPU 重新排序的问题。
换句话说:(感谢@MattBurland)
在tmp1赋值给this.Obj
之前,是否保证对象初始化器是运行?还是我需要使用内存栅栏来手动确保这一点?
C# 规范仅保证重新排序不会影响当前线程看到的内容。因此,JIT 似乎可以自由地重新排序下面的操作 2-4,因为它不会影响生产者线程的行为:
- 新建一个
MyObject
- 分配
i
给成员A
- 分配
2
给成员B
- 将新对象分配给
this.Obj
因此,似乎第 3 步和第 4 步之间需要一个障碍。另一种选择是 this.Obj
volatile
。这将确保在写入 this.Obj
之后不允许移动其他读取或写入,同时强制执行所需的顺序。