Mad about .NET A blog from Jose Fco Bonnin


Yesterday I was viewing a video (Spanish) that explains about how to implement the Singleton pattern in .NET. After some implementations the video ends up showing the best way to have a thread safe implementation of the singleton pattern.

The problem is the video ends saying that the next two implementations are exactly the same on a functional level, which is not completely true.

Implementation 1

   1:  public sealed class Singleton1
   2:  {
   3:      private DateTime creationTime;
   4:      private static Singleton1 instance = null;
   5:      private static object synchro = new object();
   6:   
   7:      private Singleton1()
   8:      {
   9:          creationTime = System.DateTime.Now;
  10:      }
  11:   
  12:      public static Singleton1 Instance
  13:      {
  14:          get
  15:          {
  16:              if (instance == null)
  17:              {
  18:                  lock (synchro)
  19:                  {
  20:                      if (instance == null)
  21:                      {
  22:                          instance = new Singleton1();
  23:                      }
  24:                  }
  25:              }
  26:   
  27:              return instance;
  28:          }
  29:      }
  30:   
  31:      public string CreationTime
  32:      {
  33:          get
  34:          {
  35:              return creationTime.ToString("hh:mm:ss.ffffff");
  36:          }
  37:      }
  38:  }

Implementation 2

   1:  public sealed class Singleton2
   2:  {
   3:      private DateTime creationTime;
   4:      private static Singleton2 instance = new Singleton2();
   5:      
   6:   
   7:      private Singleton2()
   8:      {
   9:          creationTime = System.DateTime.Now;
  10:      }
  11:   
  12:      public static Singleton2 Instance
  13:      {
  14:          get
  15:          {
  16:              return instance;
  17:          }
  18:      }
  19:   
  20:      public string CreationTime
  21:      {
  22:          get
  23:          {
  24:              return creationTime.ToString("hh:mm:ss.ffffff");
  25:          }
  26:      }
  27:  }

The second implementation is correct, since it shows how you can avoid the lock while creating the object instance of the Singleton class. The reason why this is thread safe is because .NET guarantees that the Type Constructor (or Type Initializer on the ECMA standard) is executed only once per AppDomain and is thread-safe.

Regardless both of them are thread-safe, the difference is that the first one is lazy loaded while the second one is eager loaded. In order to implement the singleton pattern using a lazy loaded version and still take profit of the Type Constructors, you need to add a container for the object instance of Singleton2. Doing this the Type Constructor will only be initialized when you access the property (or any other member making reference to it). Below you can see the correct implementation for it.

   1:  public sealed class LazyLoading
   2:  {
   3:      private DateTime creationTime;
   4:   
   5:      private LazyLoading()
   6:      {
   7:          creationTime = DateTime.Now;
   8:      }
   9:   
  10:      public static LazyLoading Instance
  11:      {
  12:          get
  13:          {
  14:              return SingletonContainer.instance;
  15:          }
  16:      }
  17:   
  18:      public string CreationTime
  19:      {
  20:          get
  21:          {
  22:              return creationTime.ToString("hh:mm:ss.ffffff");
  23:          }
  24:      }
  25:   
  26:      class SingletonContainer
  27:      {
  28:          internal static readonly LazyLoading instance = 
  29:              new LazyLoading();
  30:      }
  31:  }



Comments

January 28 2010

FCM

Hi,
According to the C# standard:
"17.4.5.1 Static field initialization
The static field variable initializers of a class declaration correspond to a sequence of assignments that are
executed in the textual order in which they appear in the class declaration. If a static constructor (§17.11)
exists in the class, execution of the static field initializers occurs immediately prior to executing that static
constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to
the first use of a static field of that class"
Therefore, in Singleton2 and LazyLoading, when the singleton instance is created is implementation-dependent.
The solution would be to add an empty static constructor to Singleton2.
Again from the standard: "The execution of a static constructor is triggered by the first
of the following events to occur within an application domain:
• An instance of the class is created.
• Any of the static members of the class are referenced."
This means that we will get the desired "lazy loading" effect.

FCM

January 31 2010

Jose Fco Bonnin

Hi FCM,

You are right in the interpretation of the standard, as you mention the creation of the instance will be implementation-dependent. But this is exactly the problem why you do not get Lazy Loading while accessing the property in all cases no matter the addition of the static constructor.

Just imagine Singleton2 have other static methods/fields, whenever you access those members the TypeConstructor of Singleton2 will be executed and Instance will be created.

Instead with LazyLoading, regardless what other members you access the type constructor of SingletonContainer will be executed (and therefore "Instance" instantiated) only when you access the property.

You can easily see the difference in the implementations if you add a static method/property to Singleton2/LazyLoading and you make use of both classes doing something similar to:

var sName = Singleton2.Name;
Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.ffffff"));
System.Threading.Thread.Sleep(3000);
Console.WriteLine(Singleton2.Instance.CreationTime);

var lName = LazyLoading.Name;
Console.WriteLine(DateTime.Now.ToString("hh:mm:ss.ffffff"));
System.Threading.Thread.Sleep(3000);
Console.WriteLine(LazyLoading.Instance.CreationTime);

Console.ReadKey();

Jose Fco Bonnin

Add comment


(Will show your Gravatar icon)

  Country flag

Captcha Image biuquote
  • Comment
  • Preview
Loading