Async singleton initialization

Still in these days you once in a while have use for a singleton object. But how do you achieve a thread-safe singleton that requires async initialization?

First, let’s start off with a good ol’ singleton class that can be constructed synchronously.

Synchronous Singleton

Assuming your singleton class needs some initialization data that it can construct itself (synchronously). To make it thread-safe, we rely on locking on a static read-only object.

In the version above the Singleton property is designed to work as quick as possible, only entering the lock if there is a chance that the class hasn’t yet been initialized. Within the lock, a method that creates the MySingleton object is called. This way it’s ensured that only one instance of the MySingleton is ever created.

Asynchronous Singleton

But what to do if the CreateSomeData method would work asynchronous, returning a Task<SomeData> instead, using the await keyword? Let’s modify the code in this way:

Making this change, we should play nice and propagate the async’ness to the CreateSingleton method:

That would naturally imply that the Singleton property should also be asynchronous, returning a Task<MySingleton> instead.

So, is that possibly?

First of all, it turns out that it’s not allowed to use the async keyword with a property. But this is no biggie, we could afford a method instead.

But more importantly, we are not allowed to use a lock around an await keyword. (This is actually a good thing, since the lock would really work against us here – so we should thank the C# team for not allowing this!)

So how do we do this then? As a starter, a naïve, non thread-safe solution would look like this:

Requesting the Singleton property would of course lead to a call to the CreateSingleton method every time, and would hardly be a singleton…

But the solution to make it both a singleton and thread-safe turns out to be ridiculously simple – we 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. Now 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, a Task is thread-safe, and it looks as if we could use this power in our advantage here!

In the end, all we need is to replace the old Singleton property with this one:

Or, rewritten in a more classic C# way (without making use of the fancy new C# 6 “read-only properties” and “property initializers”):

So the dead simple solution is to assign the async method to a read-only (static) Task field (or read-only property in C# 6). This gives you both a singleton and thread-safety for free!

Lazy Synchronous Singleton

Being empowered with the super-simple async version, you might wonder if there really is no similar way to achieve this in the synchronous version?

Of course there is!

Revisiting the synchronous first version of the MySingleton in this blog post, you may replace the SyncObj and _singleton fields and the Singleton property with these two lines:

Or, if you prefer the pre-C# 6 code:

Wow, magic! No need for locks or anything! So how does this work?

Well, the Lazy<T> can be seen as the synchronous counterpart to the Task<T> in some aspects. At least it has the same singleton and thread-safety properties, and keeps its results for latecomers.

As you can see, the constructor of the Lazy<MySingleton> is given the means of producing a MySingleton (i.e. the CreateSingleton method) – not an instance of the type directly. Then, when someone is requesting the Singleton property, the Value of the Lazy-class is accessed. And in the same manner as the Task, the first one reaching for the Value will make the actual call to the CreateSingleton method. Any other thread asking at the same time will simply be hanging in the Value getter, and continues once the CreateSingleton method is done and the Value is produced. Further on, any consecutive calls to the Value getter will return immediately with the same instance of the MySingleton. So, again, the Lazy gives us both a singleton and thread-safety.

And there was much rejoicing!

Author: Mattias

Senior .NET software consultant.

Leave a Reply