I know that the .NET memory model (on the .NET Framework; not compact/micro/silverlight/mono/xna/what-have-you) guaranteed that for certain types (most notably primitive integers and references) operations were guaranteed to be atomic.
Further, I believe that the x86/x64 test-and-set instruction (and Interlocked.CompareExchange
) actually references the global memory location, so if it succeeds another Interlocked.CompareExchange
would see the new value.
Finally, I believe that the volatile
keyword is an instruction to the compiler to propagate reads & writes ASAP and to not reorder operations concerning this variable (right?).
This leads to a few questions:
Interlocked.Read
does not have an overload for int, only for longs (which are 2 WORDs and thus are not normally read atomically). I always assumed that the .NET memory model guaranteed that the newest value would be seen when reading ints/references, however with processor caches, registers, etc. I'm starting to see this may not be possible. So is there a way to force the variable to be re-fetched?If there are two global integer variables x and y, both initialized to 0 that if I write:
x = 1;
y = 2;
That NO thread will see x = 0 and y = 2 (i.e. the writes will occur in order). Does this change if they are volatile?
A volatile variable is a variable that is marked or cast with the keyword "volatile" so that it is established that the variable can be changed by some outside factor, such as the operating system or other software.
There's no reason for a volatile variable to be stored in any "special" section of memory. It is normally stored together with any other variables, including non-volatile ones. If some compiler decides to store volatile variables in some special section of memory - there's nothing to prevent it from doing so.
If you write volatile variable from multiple threads without using any synchronized constructs, you are bound to get data inconsistency errors. Use volatile variables without synchronization in case of single write thread and multiple read threads for atomic operations.
The volatile keyword does not cache the value of the variable and always read the variable from the main memory. The volatile keyword cannot be used with classes or methods. However, it is used with variables. It also guarantees visibility and ordering.
Interlocked.CompareExchange
will see the updated value.x = 0
and y = 2
, and using the volatile keyword doesn't change that because the CPU is free to re-order instructions. You need a memory barrier.Summary:
Came across this old thread. The answers from Hans and wj32 are all correct except for the part regarding volatile
.
Specifically regarding your question
On x86/x64 can I assume that... If there are two global integer variables x and y, both initialized to 0 that if I write:
x = 1; y = 2;
That NO thread will see x = 0 and y = 2 (i.e. the writes will occur in order). Does this change if they are volatile?
If y
is volatile, the write to x
is guarantee to happen before the write to y
, therefore no thread will ever see x = 0
and y = 2
. That is because the write to a volatile variable has the "release semantic" (logically equivalent to the emission of a release fence), i.e. all read/write instructions before it won't move pass it. (This implies that if x is volatile but y is not, you might still see the unexpected x = 0
and y = 2
.) See the description & code example in the C# spec for more details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With