109

I have some questions regarding the the singleton pattern as documented here: http://msdn.microsoft.com/en-us/library/ff650316.aspx

The following code is an extract from the article:

using System;

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;
      }
   }
}

Specifically, in the above example, is there a need to compare instance to null twice, before and after the lock? Is this necessary? Why not perform the lock first and make the comparison?

Is there a problem in simplifying to the following?

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

         return instance;
      }
   }

Is the performing the lock expensive?

Si8
  • 9,141
  • 22
  • 109
  • 221
Wayne Phipps
  • 2,019
  • 6
  • 26
  • 31

11 Answers11

161

Performing the lock is terribly expensive when compared to the simple pointer check instance != null.

The pattern you see here is called double-checked locking. Its purpose is to avoid the expensive lock operation which is only going to be needed once (when the singleton is first accessed). The implementation is such because it also has to ensure that when the singleton is initialized there will be no bugs resulting from thread race conditions.

Think of it this way: a bare null check (without a lock) is guaranteed to give you a correct usable answer only when that answer is "yes, the object is already constructed". But if the answer is "not constructed yet" then you don't have enough information because what you really wanted to know is that it's "not constructed yet and no other thread is intending to construct it shortly". So you use the outer check as a very quick initial test and you initiate the proper, bug-free but "expensive" procedure (lock then check) only if the answer is "no".

The above implementation is good enough for most cases, but at this point it's a good idea to go and read Jon Skeet's article on singletons in C# which also evaluates other alternatives.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • The double-checked locking - link does not work anymore. – El Mac Jun 19 '14 at 09:57
  • I am sorry, I meant the other one. – El Mac Jun 19 '14 at 10:48
  • 1
    @ElMac: Skeet's website is down ATM, it will be back up in due course. I 'll keep it in mind and make sure the link still works when it does come up, thanks. – Jon Jun 19 '14 at 11:06
  • 3
    Since .NET 4.0 the `Lazy` doing this job just perfectly. – ilyabreev Jul 01 '15 at 06:29
  • That Jon Skeet article is informative but weirdly confusing about the use of `volatile` in the solution that uses double-checked locking. He omits it in his example and then offers vague/conflicting comments about it. I'm left guessing that it *should* be used (if you're going to use that flavor of singleton in the first place, which you probably shouldn't), but I'm not certain. – MarredCheese Apr 25 '21 at 02:05
  • I think it's highly debatable wether a lock operation is really that expensive and is very much context dependant. It most likely will not be a bottleneck considering your design is such that threads are not constantly fighting for locks. Other than that great answer, even 10 years later. – AsPas Jun 22 '21 at 09:08
50

The Lazy<T> version:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy
        = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance
        => lazy.Value;

    private Singleton() { }
}

Requires .NET 4 and C# 6.0 (VS2015) or newer.

EgoPingvina
  • 734
  • 1
  • 10
  • 33
andasa
  • 501
  • 4
  • 4
  • I am getting "System.MissingMemberException: 'The lazily-initialized type does not have a public, parameterless constructor.'" With this code on .Net 4.6.1/C# 6. – ttugates Dec 21 '17 at 16:55
  • @ttugates, you are right, thanks. Code updated with a value factory callback for the lazy object. – andasa Jan 16 '18 at 08:32
  • 1
    Not sure how this is thread safe cause thwo parallel threads can cause the singleton instance at the same time? Please can you elaborate? – WiredEntrepreneur May 11 '22 at 13:56
  • 1
    @WiredEntrepreneur I have been wondering the same thing, but it seems that Lazy objects are natively thread safe : https://stackoverflow.com/a/15222942 – Berthim May 11 '22 at 15:29
17

Performing a lock: Quite cheap (still more expensive than a null test).

Performing a lock when another thread has it: You get the cost of whatever they've still to do while locking, added to your own time.

Performing a lock when another thread has it, and dozens of other threads are also waiting on it: Crippling.

For performance reasons, you always want to have locks that another thread wants, for the shortest period of time at all possible.

Of course it's easier to reason about "broad" locks than narrow, so it's worth starting with them broad and optimising as needed, but there are some cases that we learn from experience and familiarity where a narrower fits the pattern.

(Incidentally, if you can possibly just use private static volatile Singleton instance = new Singleton() or if you can possibly just not use singletons but use a static class instead, both are better in regards to these concerns).

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • 1
    I really like your thinking here. Its a great way to look at it. I wish I could accept two answers or +5 this one, many thanks – Wayne Phipps Sep 07 '12 at 11:09
  • 2
    One consequence that becomes important when it's time to look at performance, is the difference between shared structures that *could* be hit concurrently and those that *will*. Sometimes we're not expecting such behaviour to happen often, but it could, so we need to lock (it only takes one failure to lock to ruin everything). Other times we know that lots of threads really will hit the same objects concurrently. Yet other times we weren't expecting there to be lots of concurrency, but we were wrong. When you need to improve performance, those with lots of concurrency take priority. – Jon Hanna Sep 07 '12 at 13:34
  • On your alternative, `volatile` is not necessary, however it should be `readonly`. See https://stackoverflow.com/q/12159698/428724 . – wezten Nov 17 '20 at 09:27
9

The reason is performance. If instance != null (which will always be the case except the very first time), there is no need to do a costly lock: Two threads accessing the initialized singleton simultaneously would be synchronized unneccessarily.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
5

Jeffrey Richter recommends following:



    public sealed class Singleton
    {
        private static readonly Object s_lock = new Object();
        private static Singleton instance = null;
    
        private Singleton()
        {
        }
    
        public static Singleton Instance
        {
            get
            {
                if(instance != null) return instance;
                Monitor.Enter(s_lock);
                Singleton temp = new Singleton();
                Interlocked.Exchange(ref instance, temp);
                Monitor.Exit(s_lock);
                return instance;
            }
        }
    }

4

In almost every case (that is: all cases except the very first ones), instance won't be null. Acquiring a lock is more costly than a simple check, so checking once the value of instance before locking is a nice and free optimization.

This pattern is called double-checked locking: http://en.wikipedia.org/wiki/Double-checked_locking

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94
4

This is called Double checked locking mechanism, first, we will check whether the instance is created or not. If not then only we will synchronize the method and create the instance. It will drastically improve the performance of the application. Performing lock is heavy. So to avoid the lock first we need to check the null value. This is also thread safe and it is the best way to achieve the best performance. Please have a look at the following code.

public sealed class Singleton
{
    private static readonly object Instancelock = new object();
    private Singleton()
    {
    }
    private static Singleton instance = null;

    public static Singleton GetInstance
    {
        get
        {
            if (instance == null)
            {
                lock (Instancelock)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}
Pranaya Rout
  • 301
  • 3
  • 6
1

You could eagerly create the a thread-safe Singleton instance, depending on your application needs, this is succinct code, though I would prefer @andasa's lazy version.

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    private Singleton() { }

    public static Singleton Instance()
    {
        return instance;
    }
}
Brian Ogden
  • 18,439
  • 10
  • 97
  • 176
0

Another version of Singleton where the following line of code creates the Singleton instance at the time of application startup.

private static readonly Singleton singleInstance = new Singleton();

Here CLR (Common Language Runtime) will take care of object initialization and thread safety. That means we will not require to write any code explicitly for handling the thread safety for a multithreaded environment.

"The Eager loading in singleton design pattern is nothing a process in which we need to initialize the singleton object at the time of application start-up rather than on demand and keep it ready in memory to be used in future."

public sealed class Singleton
    {
        private static int counter = 0;
        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }
        private static readonly Singleton singleInstance = new Singleton(); 

        public static Singleton GetInstance
        {
            get
            {
                return singleInstance;
            }
        }
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }

from main :

static void Main(string[] args)
        {
            Parallel.Invoke(
                () => PrintTeacherDetails(),
                () => PrintStudentdetails()
                );
            Console.ReadLine();
        }
        private static void PrintTeacherDetails()
        {
            Singleton fromTeacher = Singleton.GetInstance;
            fromTeacher.PrintDetails("From Teacher");
        }
        private static void PrintStudentdetails()
        {
            Singleton fromStudent = Singleton.GetInstance;
            fromStudent.PrintDetails("From Student");
        }
Jaydeep Shil
  • 1,894
  • 22
  • 21
0

Reflection resistant Singleton pattern:

public sealed class Singleton
{
    public static Singleton Instance => _lazy.Value;
    private static Lazy<Singleton, Func<int>> _lazy { get; }

    static Singleton()
    {
        var i = 0;
        _lazy = new Lazy<Singleton, Func<int>>(() =>
        {
            i++;
            return new Singleton();
        }, () => i);
    }

    private Singleton()
    {
        if (_lazy.Metadata() == 0 || _lazy.IsValueCreated)
            throw new Exception("Singleton creation exception");
    }

    public void Run()
    {
        Console.WriteLine("Singleton called");
    }
}
robert
  • 49
  • 3
0
/// <summary>
/// Thread safe singleton access to class
/// </summary>
/// <typeparam name="T"> T must be have a public parameterless ctor</typeparam>
/// <example>
///     SomeClass.sync(c => c.SomeMethod());
/// </example>
public class Singleton<T> where T : new()
{
    public static readonly object GlobalLock = new object();

    private static T inst;
    private static T Instance => inst ??= new T();

    public static void sync(Action<T> action)
    {
        lock (GlobalLock)
        {
            action(Instance);
        }
    }

    public Singleton()
    {
        if (inst != null) { throw new ApplicationException($"Singleton violation - mutiple objects created for {typeof(T).Name}"); }
    }
}