I recently came across the following post on the Resharper website. It was a discussion of double-checked locking, and had the following code:
public class Foo
{
private static volatile Foo instance;
private static readonly object padlock = new object();
public static Foo GetValue()
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Foo();
instance.Init();
}
}
}
return instance;
}
private void Init()
{
...
}
}
The post then makes the claim that
If we assume that Init() is a method used to intialize the state of
Foo, then the above code may not function as expected due to the
memory model not guaranteeing the order of reads and writes. As a
result, the call to Init() may actually occur before the variable
instance is in a consistent state.
Here are my questions:
-
It was my understanding that .NET memory model (at least since 2.0) has not required that
instancebe declared asvolatile, sincelockwould provide a full memory fence. Is that not the case, or was I misinformed? -
Isn’t read/write reordering only observable with respect to multiple threads? It was my understanding that on a single thread, the side effects would be in a consistent order, and that the
lockin place would prevent any other thread from observing something to be amiss. Am I off-base here as well?
The big problem with the example is that the first null check is not locked, so instance may not be null, but before Init has been called. This may lead to threads using instance before Init has been called.
The correct version should therefore be: