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):
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory) :
base(() => Task.Run(valueFactory)) { }
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Run(() => taskFactory()) { }
public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}
And consume it like this:
private AsyncLazy<bool> asyncLazy = new AsyncLazy<bool>(async () =>
{
await DoStuffOnlyOnceAsync()
return true;
});
Note i'm using bool
simply because you have no return type from DoStuffOnlyOnceAsync
.
Edit:
Stephan Cleary (of course) also has an implementation of this here.
Yes. Use Stephen Cleary's AsyncLazy
(available on the AsyncEx
nuget):
private static readonly AsyncLazy<MyResource> myResource = new AsyncLazy<MyResource>(
async () =>
{
var ret = new MyResource();
await ret.InitAsync();
return ret;
}
);
public async Task UseResource()
{
MyResource resource = await myResource;
// ...
}
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:
public class MyService
{
private MyService() { }
public static async Task<MyService> CreateAsync()
{
var result = new MyService();
result.Value = await ...;
return result;
}
}
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 of MyService
.