Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton Class which requires some async call

I have a Singleton Class which loads some data on its construction. The problem is that loading this data requires calling async methods, but the constructor cannot be async.

In other words, my class has following structure:

public class Singleton
{
   private static Singleton instance;

   private Singleton() 
   {
       LoadData();
   }

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

LoadData() is an async function which calls lots of async functions as well as initialization. How can I call LoadData() properly so everything initialize correctly?

like image 686
MBZ Avatar asked Sep 21 '12 16:09

MBZ


People also ask

Can Singleton be initialized asynchronously?

Another advantage of a Singleton class is that it can be initialized lazily or asynchronously.

What is a mandatory requirement for a singleton class?

Singleton Pattern The singleton class must provide a global access point to get the instance of the class. Singleton pattern is used for logging, drivers objects, caching and thread pool. Singleton design pattern is also used in other design patterns like Abstract Factory, Builder, Prototype, Facade etc.

Why You Should Avoid Singleton?

The most important drawback of the singleton pattern is sacrificing transparency for convenience. Consider the earlier example. Over time, you lose track of the objects that access the user object and, more importantly, the objects that modify its properties.

Why we use sealed class in Singleton?

Marking the class sealed prevents someone from trivially working around your carefully-constructed singleton class because it keeps someone from inheriting from the class.


1 Answers

The solution for a thread-safe, async singleton is actually super simple, if we only let the inner mechanisms of the Task class work for us!

So, how does a Task work? Let’s say you have an instance of a Task<T> and you await it once. Now the task is executed, and a value of T is produced and returned to you. What if you await the same task instance again? In this case the task just returns the previously produced value immediately in a completely synchronous manner.

And what if you await the same task instance simultaneously from multiple threads (where you would normally get a race condition)? Well, the first one (since there will be one that gets there first) will execute the task code while the others will wait for the result to be processed. Then when the result has been produced, all the await’s will finish (virtually) simultaneously and return the value.

So the solution for an async singleton that is thread-safe is actually super simple:

public class Singleton
{
    private static readonly Task<Singleton> _getInstanceTask = CreateSingleton();

    public static Task<Singleton> Instance
    {
        get { return _getInstanceTask; }
    }

    private Singleton(SomeData someData)
    {
        SomeData = someData;
    }

    public SomeData SomeData { get; private set; }

    private static async Task<Singleton> CreateSingleton()
    {
        SomeData someData = await LoadData();
        return new Singleton(someData);
    }
}

Now you can access the singleton this way:

Singleton mySingleton = await Singleton.Instance;

or

Singleton mySingleton = Singleton.Instance.Result;

or

SomeData mySingletonData = (await Singleton.Instance).SomeData;

or

SomeData mySingletonData = Singleton.Instance.Result.SomeData;

Read more here: Async singleton initialization

like image 132
Mattias Larsson Avatar answered Sep 19 '22 19:09

Mattias Larsson