Say I have a class that needs to perform some async initialization using an InitializeAsync() method. I want to make sure that the initialization is performed only once. If another thread calls this method while the initalization is already in progress, it will "await" until the first call returns.
I was thinking about the following imlementation (using SemaphoreSlim). Is there a better/simpler approach?
public class MyService : IMyService
{
private readonly SemaphoreSlim mSemaphore = new SemaphoreSlim(1, 1);
private bool mIsInitialized;
public async Task InitializeAsync()
{
if (!mIsInitialized)
{
await mSemaphore.WaitAsync();
if (!mIsInitialized)
{
await DoStuffOnlyOnceAsync();
mIsInitialized = true;
}
mSemaphore.Release();
}
}
private Task DoStuffOnlyOnceAsync()
{
return Task.Run(() =>
{
Thread.Sleep(10000);
});
}
}
Thanks!
Edit:
Since I'm using DI and this service will be injected, consuming it as a "Lazy" resource or using an async factory won't work for me (although it could be great in other use cases).
Thus, the async initialization should be encapsulated within the class and transparent to the IMyService
consumers.
The idea of wrapping the initialization code in a "dummy" AsyncLazy<>
object will do the job, although it feels a bit unnatural to me.
I'd go with
AsyncLazy<T>
(slightly modified version):And consume it like this:
Note i'm using
bool
simply because you have no return type fromDoStuffOnlyOnceAsync
.Edit:
Stephan Cleary (of course) also has an implementation of this here.
Yes. Use Stephen Cleary's
AsyncLazy
(available on theAsyncEx
nuget):Or the visual studio SDK's
AsyncLazy
if you prefer a Microsoft implementation.I have a blog post that covers a few different options for doing "asynchronous constructors".
Normally, I prefer asynchronous factory methods, because I think they're simpler and a bit safer:
AsyncLazy<T>
is a perfectly good way of defining a shared asynchronous resource (and may be a better conceptual match for a "service", depending on how it is used). The one advantage of the async factory method approach is that it's not possible to create an uninitialized version ofMyService
.