Lazy singleton in a multithreaded c# application

24,304

Solution 1

According to Microsoft's Lazy Initialization documentation, under the section titled "Thread-Safe Initialization":

By default, Lazy objects are thread-safe.

With this in mind, your abc field needn't be static. As you're using a Lazy<T> to instantiate your singleton, it's safe to initialise your connection in the CLazySingleton constructor.

Solution 2

Simple use ThreadSafetyMode

 Lazy<MergeSort> ty = new Lazy<MergeSort>(LazyThreadSafetyMode.ExecutionAndPublication);

Solution 3

Would this code be able to take care of multiple threads trying to get the instance at the same time ?

In your scenario can be the "abc" field initialized twice. Imagine situation, that "abc" variable is null. First thread will be inside the "lock" block before the value assignment. Second thread will be waiting before the lock. So the first thread will initialize the "abc" and second thread will reinitialize it (your check for null is outside of lock, that's the reason). But maybe this is not something you should be afraid of.

Can I have a better solution for this ?

Yes you can. Let me describe it in the last block of this answer.

Do I need to use 'lock' here or using Lazy approach takes care of multithreads trying to get the instance ?

Creation of the Value property in Lazy class is thread safe. In your scenario I would use the advantage of property IsValueCreated of Lazy<> class. You will still need the ThreadLock object as well. Just one more thing is, that once you access Value property of Lazy<> class, IsValueCreated property will return true (that's the trick ;-) )

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LazySingleton
{
    public class CLazySingleton
    {
        private static readonly Lazy<CLazySingleton> _instance
            = new Lazy<CLazySingleton>(() => new CLazySingleton());
        private static readonly object ThreadLock = new object();
        public static string abc;  
        //I will use the service connection object in place of 'abc' in the application
        //assume that 'abc' is storing the connection object    

        private CLazySingleton()
        { }

        public static CLazySingleton Instance
        {
            get
            {   
                if (_instance.IsValueCreated)
                {
                    return _instance.Value;
                }
                lock (ThreadLock)
                {
                    if (abc == null)
                    {
                        abc = "Connection stored in this variable";
                        Console.WriteLine("Connection Made successfully");
                    }
                }
                return _instance.Value;
            }
        }
    }
}
Share:
24,304
Anubhav Sharma
Author by

Anubhav Sharma

Updated on April 23, 2020

Comments

  • Anubhav Sharma
    Anubhav Sharma about 4 years

    I am working on a multithreaded c# application which is consuming a WCF web service. The connection to the webservice will have a specific timeout which we can define and after which it will close. I am looking to store the connection to the web service using singleton class. I am trying to get the instance as follows :

    CLazySingleton ins = CLazySingleton.Instance;
    string connection = CLazySingleton.abc;
    

    Below is the code for the singleton class :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LazySingleton
    {
        public class CLazySingleton
        {
            private static readonly Lazy<CLazySingleton> _instance
                = new Lazy<CLazySingleton>(() => new CLazySingleton());
            private static readonly object ThreadLock = new object();
            public static string abc;  
            //I will use the service connection object in place of 'abc' in the application
            //assume that 'abc' is storing the connection object    
    
            private CLazySingleton()
            { }
    
            public static CLazySingleton Instance
            {
                get
                {   
                    if (abc == null)
                    {
                        lock (ThreadLock)
                        {
                            //Make the connection
                            abc = "Connection stored in this variable";
                            Console.WriteLine("Connection Made successfully");
                            return _instance.Value;
                        }                    
                    }
                    else
                    {
                        return _instance.Value;
                    }
                }
            }
        }
    }
    

    My questions are : 1. Would this code be able to take care of multiple threads trying to get the instance at the same time ? This is currently my biggest concern. 2. Can I have a better solution for this ? 3. Do I need to use 'lock' here or using Lazy approach takes care of multithreads trying to get the instance ?

    Any help would be appreciated.

    Thanks !

  • Phil
    Phil about 11 years
    Lazy's default constructor uses LazyThreadSafetyMode.ExecutionAndPublication, it's thread safe by default.
  • Anubhav Sharma
    Anubhav Sharma about 11 years
    Hi Richard, Thanks for the response. You made a good point. Even I was pondering upon the same. But my question is, if I write the logic to make the connection in the constructor, how would I manage the scenario where the connection gets expired by itself after specified timeout. In that case, the field abc will become null and the singleton class would keep on returning that as null only as the constructor won't be called again. I hope that I could deliver my point.
  • Anubhav Sharma
    Anubhav Sharma about 11 years
    Thanks, I'll surely try this.
  • thewpfguy
    thewpfguy about 11 years
    I think using Singleton is not the right pattern here, actually you do not need to manage your connection this way. And, are you sure you are calling a web service, because what's the kind of timeout you mention here?