Is a memory fence required here?
Basically I have the following situation:
var tmp1 = new MyObject() { A = i, B = 2 };
// ** write barrier here??
this.Obj = tmp1;
Another thread can do things like this:
var tmp = this.Obj;
// ** read barrier here??
use(tmp.A);
Objects such as "Obj" are written only once, then read by multiple threads (multiple times).
I know Obj never has anywhere in both threads; I also don't care about "this.Obj" synchronization. What I really care about is that once I read the link tmp = Obj
, the content (eg A
and B
) is also valid.
My question is, do I need memory barriers (for example Thread.MemoryBarrier();
) in the above positions to make sure this is or is not always normal?
People seem to dislike this question.
My question comes from the following. I read on memory fences and they guarantee: (quote)
The processor executing the current thread cannot reorder instructions so that a memory access prior to a MemoryBarrier call occurs after a memory access following a MemoryBarrier call.
If you look at the code, the CPU / compiler will be able to rewrite 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 catches the latter case, it can read this.Obj
from memory as well A
and B
still have a default.
Note that this is not just a matter of what the compiler can change; it is also a matter of what the CPU can reorder.
In other words: (Thanks @MattBurland)
Is the object initializer guaranteed to run before tmp1 is assigned this.Obj
? Or do I have to use a memory fence to enforce this manually?
source to share
The C # spec only guarantees that the reordering cannot affect what the current stream sees. So it seems like the JIT is free to reorder the operations 2-4 below, since it doesn't affect the behavior of the producer thread:
- Create a new
MyObject
- Assign
i
memberA
- Assign
2
memberB
- Assign a new object
this.Obj
So it seems like a barrier is required between steps 3 and 4.. Another option would be to do this.Obj
volatile
. This will ensure that no other reads or writes can be moved after being written to this.Obj
, while also providing the required ordering.
source to share