Double Check Locking and Other Premature Optimizations Can Shoot You In The Foot

code 0 comments suggest edit

Lock After reading Scott Hanselman’s post on Managed Snobism which covers the snobbery some have against managed languages because they don’t “perform” well, I had to post the following rant in his comments:

What is it that makes huge populations of developers think they’re working on a Ferrari when their app is really just a Pinto? \ \ “I’m writing a web app that pulls data from a database and puts it on a web page. I never use ‘foreach’ because I heard it’s slower than explicitly iterating a for loop.

In my time as a developer I’ve experienced too many instances of this Micro Optimization, also known as Premature Optimization.

Premature optimization tends to lead “clever” developers to shoot themselves in the foot (metaphorically speaking, of course). Let’s look at one common example I’ve run into from time to time—double check locking for singletons.

Double Check Locking Refresher

As a refresher, here is an example of the double check pattern.

public sealed class MyClass
{
  private static object _synchBlock = new object();
  private static volatile MyClass _singletonInstance;

  //Makes sure only this class can create an instance.
  private MyClass() {}
  
  //Singleton property.
  public static MyClass Singleton
  {
    get
    {
      if(_singletonInstance == null)
      {
        lock(_synchBlock)
        {
          // Need to check again, in case another cheeky thread 
          // slipped in there while we were acquiring the lock.
          if(_singletonInstance == null)
          {
            _singletonInstance = new MyClass();
          }
        }
      }
    }
  }
}

The premise behind this approach is that all this extra ugly code will wring out better performance by lazy loading the singleton. If it is never accessed, it never needs to be instantiated. Of course this raises the question, Why define a Singleton if it’s quite likely it’ll never get used?

The Singleton property checks the static singleton member for null. If it is null, it attempts to acquire a lock before checking if its null again. Why the second null check? Well in the time our current thread took to acquire the lock, another thread could have snuck in and initialized the singleton.

Note that we use the volatile keyword for the _singletonInstance static member. Why? Long story made short, this has to do with how different memory models can reorder reads and writes. For the current CLR you can ignore the volatile keyword in this case. But if you run your code on Mono or some other future platform, you may need it, so no point in not leaving it there.

Criticisms or If this is fast, how much faster is triple check locking?

Jeffrey Richter in his book CLR via C# criticizes this approach (starting on page 639) as “not that interesting” (Yes, he can be scathing!)

The double-check locking technique is less efficient than the class constructor technique because you need to construct your own lock object (in the class constructor) and write all of the additional locking code yourself.

The cost of initializing the singleton instance would have to be significantly more than the cost of instantiating the object used to synchronize access to it (not to mention all the conditional checks when accessing the singleton) to be worth it.

A Better Approach? The No Look Pass of Singletons

So what’s the better approach? Use a static initializer in what I call the No Check No Locking Technique.

public sealed class MyClass
{
  private static MyClass _singletonInstance = new MyClass();

  //Makes sure only this class can create an instance.
  private MyClass() {}
  
  //Singleton property.
  public static MyClass Singleton
  {
    get
    {
      return _singletonInstance;
    }
  }
}

The CLR guarantees that the code in a static constructor (implicit or explicit) is only called once. You get all that thread safety for free! No need to write your own error prone locking code in this case and no need to dig through Memory Model implications. It just works, unlike your Pinto, sorry, “Ferrari”.

See, sometimes you can have your cake and eat it too. This code, which is simpler and easier to understand, happens to perform better and requires one less object instantiaton. How do you like them apples?

It turns out that this approach is also recommended for Java, as it was discovered that the double check locking approach wasn’t guaranteed to work.

What!? You’re Still Using Singletons?!

Now that I’ve gone through all this trouble to show you the proper way to create a Singleton, I leave you with this thought. Should a well designed system use Singletons in the first place, or is it just a stupid idea? That’s a topic for another time.

Please note that double check locking doesn’t only apply to Singletons. It just happens to be the place where it is most often seen in the wild.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

8 responses

  1. Avatar for Julian Birch
    Julian Birch March 19th, 2007

    Global state/variables have their place. However, the thing that I find most disturbing is that very few implementations have any facilities for reloading the state if, for instance, the underlying database has changed. Fewer still have any support for ensuring that running threads do not end up accessing inconsistent versions during a reset.
    I blame the Gang of Four, personally. The Singleton pattern doesn't deserve to live.

  2. Avatar for Thomas Wagner
    Thomas Wagner March 19th, 2007

    Its 6:02AM and I havent had enough coffee - BUT - the interesting thing about your post is that it looks a lot like some code for the Provider base class in the CLR if I remember correctly. Ironic isn't it. (Great point by the way)

  3. Avatar for Tom Opgenorth
    Tom Opgenorth March 19th, 2007

    An interesting article article on singletons: http://www.yoda.arachsys.co.... Discusses several patterns.

  4. Avatar for Martin
    Martin March 20th, 2007

    Very nice article and clean examples.
    I think the reason I like double-check-locking is the true lazy construction, the class construction is only done when a calling client makes it happen.
    Static loading of a class can happen in many ways. For example, take a class, add some static work, remote that class on a server and reference it on the client.
    The activation on the client to the server object, will also construct the objects static work *ON* the client.
    easy fix, remote interfaces but hay, not everyone does it

  5. Avatar for lb
    lb March 23rd, 2007

    i'm just mesmerized by those pretty identicons.
    oh wait, that's what i was going to say:
    regarding:
    "...programmers shoot themselves in the foot (metaphorically speaking, of course"
    i'm glad you said 'metaphorically'. For a moment there i thought that there was a widespread spate of developers going around actually blowing their own appendages away with shotguns and the like. Phew.
    and i feel all kind of embarrassed about the double-check locking thing. i'll say no more. just quietly update a few files in a few projects. ;-)

  6. Avatar for Haacked
    Haacked March 23rd, 2007

    Leon, I don't know if I could live with myself if I someday meet you and you only had one foot. I'd feel really bad.
    So I'm really really reallyglad I covered my ass and added that in there!

  7. Avatar for Hugh Moran
    Hugh Moran June 14th, 2007

    Thank you Phil !
    I only came across this 'double checked locking' discussion recently. The last time I saw anything like it was in 1987 when I first began to write multithreaded shared memory code. I dreamt it up when designing some API and 30 minutes later discarded it, realsiing it was clouded thinking!
    Why do it's advocates never ask themselves "Hmm I wonder why I never see this in device drivers or kernels?"
    Surely it would be in them if there were no other (better) way?
    Even if it were necessary to handle singleton creation 'manually' like this, it is far better to simply use InterlockedCompareExchange on some static private integer as the means of testing.
    By definition this could ONLY return its initial value ONCE and is easy to understand.
    I don't know Dave Cutler, but I'd be a bit surprised if he didnt think 'double checked locking' was a load of *&$%^!
    Regards
    Hugh

  8. Avatar for Reshhmi
    Reshhmi April 9th, 2008

    Thanks for this article. It really helped me.
    I needed a thread safe singleton and was recomended the double locking theory.