Joe Duffy, gives 6 rules that describe the CLR 2.0+ memory model (it\'s actual implementation, not any ECMA standard) I\'m writing down my attempt at figuring this out, mostly a
Joe Duffy discusses Rule 5 on pp517-18 of Concurrent Programming on Windows:
As an example of when a load might be introduced, consider this code:
MyObject mo = ...;
int f = mo.field;
if (f == 0)
{
// do something
Console.WriteLine(f);
}
If the period of time between the initial read of mo.field into variable f and the subsequent use of f in the Console.WriteLine was long enough, a compiler may decide it would be more efficient to reread mo.field twice. ... Doing this would be a problem if mo is a heap object and threads are writing concurrently to mo.field. The if-block may contain code that assumes the value read into f remained 0, and the introduction of reads could break this assumption. In addition to prohibiting this for volatile variables, the .NET memory model prohibits it for ordinary variables referring to GC heap memory too.
I blogged about one important place where this matters: the standard pattern for raising an event.
EventHandler handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
In order to prevent problems with removing an event handler on a separate thread, we read the current value of MyEvent
and only invoke the event handlers if that delegate is non-null.
If reads from the heap could be introduced, the compiler/JIT might decide that it could be better to read MyEvent
again, rather than using the local, which would introduce a race condition.