singleton design pattern

Key points

  • Singleton is a class designed to have one and only one instance
  • These are often required to model some kind of shared resource(like file system , access to shared network resource). So better to create only one instance. Better to incur the cost only once
  • At any point of time only 0 or 1 instance of instance of the singleton class exists in the application
  • For performance reasons singleton classes are often not created until something requests them. – LAZY Instantiation…
  • Singleton class shoule have single , private , parameterless constructor
  • Because of this subclass is not allowed
  • To further enforce this intent – mark the class as sealed
  • A private static field holds the only reference to the instance. A public Static method that this class exposes will have access to this field.
  • Test cases passes
  • But there is one issue in multithreading. The constructor is invoked more than once.
  • We have to use lock to ensure the constructor is invoked only once
  • Use double lock- so that lock happens only at that start of the application. 
				
					#nullable enable
public sealed class Singleton
{
    public static int constructorInvokedCount = 0;
    public static int instanceInvoked = 0;
    private static Singleton? _instance;

    public static Singleton Instance
    {
        get
        {
            instanceInvoked++;
            return _instance ?? (_instance = new Singleton());
        }
    }

    private Singleton()
    {
        constructorInvokedCount++;
        Console.Write("Constructor invoked");
    }
    public static void Main(string[] args)
    {

    }
}
				
			
				
					using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace TestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Single_Instance_Is_Not_Null()
        {
            var result = Singleton.Instance;
            Assert.IsNotNull(result);
        }


        [TestMethod]
        public void Only_One_Instance_Of_Singleton()
        {
            var result1 = Singleton.Instance;
            var result2 = Singleton.Instance;
            var result3 = Singleton.Instance;
            Assert.AreEqual(1, Singleton.constructorInvokedCount);
            Assert.AreEqual(3, Singleton.instanceInvoked);
        }

        [TestMethod]
        public void More_Than_One_Instance_Of_Singleton_In_MultiThreading()
        {
            var instances = new List<Singleton>();
            var options = new ParallelOptions { MaxDegreeOfParallelism = 3 };
            var strings = new List<int>() { 1, 2, 3 };
            Parallel.ForEach(strings, options, instance =>
             {
                 instances.Add(Singleton.Instance);
             });
            System.Console.WriteLine(Singleton.constructorInvokedCount);
            Assert.IsTrue(Singleton.constructorInvokedCount > 1);//3
            Assert.AreEqual(3, Singleton.instanceInvoked);
        }
    }
}
				
			
				
					 public static Singleton Instance
    {
        get
        {
            instanceInvoked++;

            lock (padlock)
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
                return _instance;
            }

        }
    }
				
			
				
					 public static Singleton Instance
    {
        get
        {
            instanceInvoked++;

            if (_instance == null)
            {
                lock (padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton();
                    }
                }
            }
            return _instance;
        }
    }
				
			
  • Locking adds thread safety
  • Initial approach imposes lock on every access, not just first time
  • Double null check :- Ensures the lock happens only at the start….after that instance is not null so lock never happens again
  • Commented some code. make the instance static readonly….get rid of the mutex and lock
  • this is thread safe, coz the way the static initializes work in c# and .net is that only one thread can be in static initializer and the .net framework CLR manages to make sure that its pretty efficient and it is equivalent to double check locking
  •  c# static constructors only run once per appdomain
  •  Static constructors are called when any static memeber of a type is referenced
  •  Static constructors – thread safe, no locks – good performance
  • quick view on static constructor…
  • Used to initialize the static members of a class.
  • Can not access non-static members.
  • Executes before the first instance of a class. We can not determine the time of execution.
  • Executes by the CLR not by the object of a class.
  • There are no parameterized static constructors since it is handled by the CLR not by the object.
  • Time of execution might be at the loading of contained assembly.
  1.  
				
					#nullable enable
public sealed class Singleton
{
    public static int constructorInvokedCount = 0;
    public static int instanceInvoked = 0;
    //private static Singleton? _instance;
    private static readonly Singleton instance = new Singleton();
    //private static readonly object padlock = new object();

    //public static Singleton Instance
    //{
    //    get
    //    {
    //        instanceInvoked++;

    //        if (_instance == null)
    //        {
    //            lock (padlock)
    //            {
    //                if (_instance == null)
    //                {
    //                    _instance = new Singleton();
    //                }
    //            }
    //        }
    //        return _instance;
    //    }
    //}

    public static Singleton Instance { 
        get {
            instanceInvoked++;
            return instance; 
        } 
    }

    private Singleton()
    {
        constructorInvokedCount++;
        Console.Write("Constructor invoked");
    }
    public static void Main(string[] args)
    {

    }
}

				
			

Leave a Comment