When I write a value into a field, what guarantees do I get regarding when the new value will be saved in the main memory? For example, how do I know that the processor don't keep the new value in it's private cache, but updated the main memory?
Another example:
int m_foo;
void Read() // executed by thread X (on processor #0)
{
Console.Write(m_foo);
}
void Write() // executed by thread Y (on processor #1)
{
m_foo = 1;
}
Is there a possibility that after Write() was finished executing, some other thread executes Read() but actually will see "0" as the current value? (since perhaps the previous write to m_foo wasn't flushed yet?).
What kind of primitives (beside locks) are available to ensure the the writes were flushed?
EDIT
In the code sample I've used, the write and read are placed at different method. Doesn't Thread.MemoryBarrier only affect instruction reording that exist in the same scope?
Also, let's assume that they won't be inlined by the JIT, how can I make sure that the value written to m_foo won't be stored in a register, but to the main memory? (or when m_foo is read, it won't get an old value from the CPU cache).
Is it possible to achieve this without using locks or the 'volatile' keyword? (also, let's say I'm not using primitive types, but a WORD sized structs [so volatile cannot be applied].)
Volatile and Interlocked have already been mentioned, you asked for primitives, one addition to the list is to use
Thread.MemoryBarrier()
before your writes or reads. This guarantees no reordering is done of memory writes and reads.This is doing "by hand" what
lock
,Interlocked
andvolatile
can do automatically most of the time. You could use this as a full replacement to any other technique, but it is arguably the hardest path to travel, and so says MSDN:How to use MemoryBarrier
A very fine example are the implementations of
VolatileRead
andVolatileWrite
, that both internally useMemoryBarrier
. The basic rule of thumb to follow is: when you read a variable, place a memory barrier after the read. When you write the value, the memory barrier must come before the write.In case you've doubts whether this is less efficient then
lock
, consider that locking is nothing more then "full fencing", in that it places a memory barrier before and after the code block (ignoring Monitor for a moment). This principle is well explained in this excellent definitive article on threads, locking, volatile and memory barriers by Albahari.From reflector: