3

Basically I have the following situation:

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

Another thread can do stuff like this:

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

Objects like 'Obj' are only written once, then read by multiple threads (multiple times).

I know that Obj is never null in both threads; I also don't care about the synchronization of 'this.Obj'. What I do care about is that once I read the reference tmp = Obj, the contents (e.g. A and B) are also valid.

My question is: Do I need memory barriers (e.g. Thread.MemoryBarrier();) at the above marked positions to ensure that or is this always implicitly OK?


It seems that people dislike this question.

My question originates from the following. I've read up on memory fences and they guarantee: (quote)

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.

If you look at the code, the CPU / compiler might be able to re-write the code:

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

and worse:

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

If another thread picks up the last case, it can read this.Obj from memory, while A and B still have the default value.

Note that it's not just a matter of what the compiler is able to reorder; it's also a matter of what the CPU is allowed to reorder.

In other words: (Thanks @MattBurland )

Is it guaranteed that the object initializer will be run before tmp1 is assigned to this.Obj? Or do I need to use a memory fence to ensure this manually?

4

1 回答 1

1

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

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

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

于 2015-07-24T13:38:28.897 回答