views:

519

answers:

8

Multiple texts say that when implementing double-checked locking in .NET the field you are locking on should have volatile modifier applied. But why exactly? Considering the following example:

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}

why doesn't "lock (syncRoot)" accomplish the necessary memory consistency? Isn't it true that after "lock" statement both read and write would be volatile and so the necessary consistency would be accomplished?

A: 

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Terry Mahaffey
Wrong language and wrong conclusion imo.
Benjamin Podszun
It's for a different language, but it's applicable. volatile in c# and java have the same semantics.
Terry Mahaffey
@Terry: Whether or not the `volatile` keyword has the same semtantics in C# and Java is pretty much irrelevant. What matters is that the CLR and the JVM have *different* memory models.
LukeH
A: 

This a pretty good post about using volatile with double checked locking:

http://tech.puredanger.com/2007/06/15/double-checked-locking/

In Java, if the aim is to protect a variable you don't need to lock if it's marked as volatile

Scobal
Interesting, but not necessarily very helpful. The JVM's memory model and the CLR's memory model are not the same thing.
bcat
A: 

AFAIK (and - take this with caution, I'm not doing a lot of concurrent stuff) no. The lock just gives you synchronization between multiple contenders (threads).

volatile on the other hand tells your machine to reevaluate the value every time, so that you don't stumble upon a cached (and wrong) value.

See http://msdn.microsoft.com/en-us/library/ms998558.aspx and note the following quote:

Also, the variable is declared to be volatile to ensure that assignment to the instance variable completes before the instance variable can be accessed.

A description of volatile: http://msdn.microsoft.com/en-us/library/x13ttww7%28VS.71%29.aspx

Benjamin Podszun
A 'lock' also provides a memory barrier, the same as (or better than) volatile.
Henk Holterman
A: 

This link I think is an excellent explanation: http://www.ibm.com/developerworks/java/library/j-dcl.html

GregS
+10  A: 

Volatile is unnecessary. Well, sort of**

volatile is used to create a memory barrier* between reads and writes on the variable.
lock, when used, causes memory barriers to be created around the block inside the lock, in addition to limiting access to the block to one thread.
Memory barriers make it so each thread reads the most current value of the variable (not a local value cached in some register) and that the compiler doesn't reorder statements. Using volatile is unnecessary** because you've already got a lock.

Joseph Albahari explains this stuff way better than I ever could.

And be sure to check out Jon Skeet's guide to implementing the singleton in C#


update:
*volatile causes reads of the variable to be VolatileReads and writes to be VolatileWrites, which on x86 and x64 on CLR, are implemented with a MemoryBarrier. They may be finer grained on other systems.

**my answer is only correct if you are using the CLR on x86 and x64 processors. It might be true in other memory models, like on Mono (and other implementations), Itanium64 and future hardware. This is what Jon is referring to in his article in the "gotchas" for double checked locking.

Doing one of {marking the variable as volatile, reading it with Thread.VolatileRead, or inserting a call to Thread.MemoryBarrier} might be necessary for the code to work properly in a weak memory model situation.

From what I understand, on the CLR (even on IA64), writes are never reordered (writes always have release semantics). However, on IA64, reads may be reordered to come before writes, unless they are marked volatile. Unfortuantely, I do not have access to IA64 hardware to play with, so anything I say about it would be speculation.

i've also found these articles helpful:
http://www.codeproject.com/KB/tips/MemoryBarrier.aspx
vance morrison's article (everything links to this, it talks about double checked locking)
chris brumme's article (everything links to this)
Joe Duffy: Broken Variants of Double Checked Locking

luis abreu's series on multithreading give a nice overview of the concepts too
http://msmvps.com/blogs/luisabreu/archive/2009/06/29/multithreading-load-and-store-reordering.aspx
http://msmvps.com/blogs/luisabreu/archive/2009/07/03/multithreading-introducing-memory-fences.aspx

dan
The first link is actually very useful, thanks ;)
Konstantin
Jon Skeet actually says that volatile modifier is needed to create proper memory barier, while the first link author says that lock (Monitor.Enter) would be sufficient. Who is actually right???
Konstantin
@Konstantin it seems that Jon was referring to the memory model on Itanium 64 processors, so in that case, using volatile may be necessary. However, volatile is unnecessary on x86 and x64 processors. I'll update more in a little bit.
dan
If lock is indeed making a memory barrier and if memory barier is indeed about both instruction order AND cache invalidating, then it should work on all processors. Anyway it's so strange that such basic thing causes so much confusion...
Konstantin
I think this is a very extensive coverage of the problem, especially provided links are very useful. My only note is that, as far as I understand from all articles, .NET memory model works the same on all processors, but other CLI implementations (such as Mono) could use other (weaker) memory model. This is achieved using JIT compiler generated instructions.
Konstantin
+2  A: 

I don't think anybody has actually answered the question, so I'll give it a try.

The volatile and the first if (instance == null) are not "necessary". The lock will make this code thread-safe.

So the question is: why would you add the first if (instance == null)?

The reason is presumably to avoid executing the locked section of code unnecessarily. While you are executing the code inside the lock, any other thread that tries to also execute that code is blocked, which will slow your program down if you try to access the singleton frequently from many threads. Depending on the language/platform, there could also be overheads from the lock itself that you wish to avoid.

So the first null check is added as a really quick way to see if you need the lock. If you don't need to create the singleton, you can avoid the lock entirely.

But you can't check if the reference is null without locking it in some way, because due to processor caching, another thread could change it and you would read a "stale" value that would lead you to enter the lock unnecessarily. But you're trying to avoid a lock!

So you make the singleton volatile to ensure that you read the latest value, without needing to use a lock.

You still need the inner lock because volatile only protects you during a single access to the variable - you can't test-and-set it safely without using a lock.

Now, is this actually useful?

Well I would say "in most cases, no".

If Singleton.Instance could cause inefficiency due to the locks, then why are you calling it so frequently that this would be a significant problem? The whole point of a singleton is that there is only one, so your code can read and cache the singleton reference once.

The only case I can think of where this caching wouldn't be possible would be when you have a large number of threads (e.g. a server using a new thread to process every request could be creating millions of very short-running threads, each of which would have to call Singleton.Instance once).

So I suspect that double checked locking is a mechanism that has a real place in very specific performance-critical cases, and then everybody has clambered on the "this is the proper way to do it" bandwagon without actually thinking what it does and whether it will actually be necessary in the case they are using it for.

Jason Williams
+1  A: 

The lock is sufficient. The MS language spec (3.0) itself mentions this exact scenario in §8.12, without any mention of volatile:

A better approach is to synchronize access to static data by locking a private static object. For example:

class Cache
{
    private static object synchronizationObject = new object();
    public static void Add(object x) {
        lock (Cache.synchronizationObject) {
    ...
        }
    }
    public static void Remove(object x) {
        lock (Cache.synchronizationObject) {
    ...
        }
    }
}
Marc Gravell
Jon Skeet in his article (http://www.yoda.arachsys.com/csharp/singleton.html) says that volatile is needed for proper memory barrier in this case. Marc, can you comment on this?
Konstantin
Ah, I hadn't noticed the double-checked lock; simply: don't do that ;-p
Marc Gravell
I actually think that double-checked lock is a good thing performance-wise. Also if it is necessary to make a field volatile, while it is accessed within lock, then double-cheked lock is not much worse than any other lock...
Konstantin
But is it as good as the separate class approach Jon mentions?
Marc Gravell
A: 

I think that I've found what I was looking for. Details are in this article - http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S10.

To sum up - in .NET volatile modifier is indeed not needed in this situation. However in weaker memory models writes made in constructor of lazily initiated object may be delayed after write to the field, so other threads might read corrupt non-null instance in the first if statement.

Konstantin