Partager via


Lazy init singleton

I’ve seen some confusion in some MS internal mailing lists about when singletons are instantiated for the pattern described in:

 

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/singletondespatt.asp

 

 

// .NET Singleton

sealed class Singleton

{

    private Singleton() {}

    public static readonly Singleton Instance = new Singleton();

}

 

The confusion comes from trying to understand when the singleton’s instance is created. The article states:

 

‘What we really care about is that we get the instance created either on or just before the first call to (in this case) the Instance property, and that we have a defined initialization order for static variables in a class’

 

With the code above, the correct answer is ‘Whenever the CLR decides so, as long as it happens before the first use of Singleton.Instance’. This is not what you really want if your singleton is bringing in some expensive resource and the text in the article if not wrong, may be a bit misleading.

 

The following counterexample initializes the singleton even if we will never call Instance.

 

using System;

 

sealed class Singleton

{

    private Singleton()

    {

            Console.WriteLine("Singleton initialized");

    }

   

    public void Method()

    {

            Console.WriteLine("Method");

    }

    public static readonly Singleton Instance = new Singleton();

}

 

class Test

{

            static public bool bFalse = false;

            static public void Main()

            {

                        if (bFalse)

                        {

                                    Singleton.Instance.Method();

                        }

            }

}

 

Why?

 

The compiler embeds the static initialization

 

public static readonly Singleton Instance = new Singleton();

 

inside the class constructor (.cctor) of the class Singleton. So the question now is ‘when does the .cctor get run’. I go over this in https://blogs.msdn.com/davidnotario/archive/2005/02/08/369593.aspx . If you really want a singleton that only gets created on first access, you want the Singleton type to have precise init. You would do it this way:

 

 

sealed class Singleton

{

    Static Singleton()

    {

    }

    private Singleton()

    {

            Console.WriteLine("Singleton initialized");

    }

   

    public void Method()

    {

            Console.WriteLine("Method");

    }

    public static readonly Singleton Instance = new Singleton();

}

 

Now, note this behavior may come at the expense of runtime checks (that ask the 'did my .cctor run already?' question), whereas with beforefieldinit semantics, the .cctor gets run aggressively by the JIT (at compile time) and no checks need to be inserted (so maybe you want 2 singleton classes, Singleton and ExpensiveResourceSingleton, depending on your needs).

Comments

  • Anonymous
    July 19, 2005
    This seems like an over-complication to me... what's wrong witht this?

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

    static Singleton instance;
  • Anonymous
    July 19, 2005
    From the msdn article I link:

    Let’s examine this code for a moment. This simple class has one member variable and that is a pointer to itself. Notice that the constructor is protected and that the only public method is the Instance method. In the implementation of the Instance method, there is a control block (if) that checks to see if the member variable has been initialized, and if not creates a new instance. This lazy initialization in the control block means that the Singleton instance is initialized, or created, only on the first call to the Instance() method. For many applications, this approach works just fine. But, for multithreaded applications, this approach proves to have a potentially hazardous side effect. If two threads manage to enter the control block at the same time, two instances of the member variable could be created. To solve this, you might be tempted to merely place a critical section around the control block in order to guarantee thread safety. If you do this, then all calls to the Instance method would be serialized and could have a very negative impact on performance, depending on the application.
  • Anonymous
    July 19, 2005
    The thread-safe access easily resolved by interlocked double-checking. This is described many times, see C++ example in the 'Modern C++ Design' by A.Alexandrescu.

    public static Singleton Instance
    {
    get
    {
    // Prevent unnecessary locking.
    if (instance == null)
    {
    lock (typeof(Singleton))
    {
    // Check whether this is still true (in a case of multiple treads
    // entering the previous if... block simultaneously.
    if (instance == null)
    {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }
  • Anonymous
    July 19, 2005
    It is highly discouraged to lock a public field, locking based on the type can cause deadlock that is out of the the type authors control.

    It is always better to create a private static field and lock on that.
  • Anonymous
    July 24, 2005
    Blog link of the week 29