It is generally accepted (I believe!) that a lock will force any values from fields to be reloaded (essentially acting as a memory-barrier or fence – my terminology in this area gets a bit loose, I’m afraid), with the consequence that fields that are only ever accessed inside a lock do not themselves need to be volatile.
(If I’m wrong already, just say!)
A good comment was raised here, questioning whether the same is true if code does a Wait() – i.e. once it has been Pulse()d, will it reload fields from memory, or could they be in a register (etc).
Or more simply: does the field need to be volatile to ensure that the current value is obtained when resuming after a Wait()?
Looking at reflector, Wait calls down into ObjWait, which is managed internalcall (the same as Enter).
The scenario in question was:
bool closing;
public bool TryDequeue(out T value) {
lock (queue) { // arbitrary lock-object (a private readonly ref-type)
while (queue.Count == 0) {
if (closing) { // <==== (2) access field here
value = default(T);
return false;
}
Monitor.Wait(queue); // <==== (1) waits here
}
...blah do something with the head of the queue
}
}
Obviously I could just make it volatile, or I could move this out so that I exit and re-enter the Monitor every time it gets pulsed, but I’m intrigued to know if either is necessary.
Since the
Wait()method is releasing and reacquiring theMonitorlock, iflockperforms the memory fence semantics, thenMonitor.Wait()will as well.To hopefully address your comment:
The locking behavior of
Monitor.Wait()is in the docs (http://msdn.microsoft.com/en-us/library/aa332339.aspx), emphasis added:If you’re asking about a reference for whether a
lock/acquiredMonitorimplies a memory barrier, the ECMA CLI spec says the following:12.6.5 Locks and Threads:
12.6.7 Volatile Reads and Writes:
Also, these blog entries have some details that might be of interest: