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?
Another advantage of a Singleton class is that it can be initialized lazily or asynchronously.
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.
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.
Marking the class sealed prevents someone from trivially working around your carefully-constructed singleton class because it keeps someone from inheriting from the class.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With