同步访问单例对象初始化

Synchronise access to singleton object initialisation

我正在从 dotnet 核心 Web 应用程序访问 EventStore。所有线程共享一个连接。连接在第一次访问时打开,我需要确保只有一个线程打开连接。以前我会使用 lock 但后来我无法 await 打开连接的方法。

我发现 following snippet 代码看起来应该可以解决问题:

public class AsyncLock : IDisposable
{
    private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);

    public async Task<AsyncLock> LockAsync()
    {
        await _semaphoreSlim.WaitAsync().ConfigureAwait(false);
        return this;
    }

    public void Dispose()
    {
        _semaphoreSlim.Release();
    }
}

并在我的代码中使用它:

private static readonly AsyncLock _mutex = new AsyncLock();
private volatile bool _isConnected = false;
private async Task EstablishConnected()
{
    if (!_isConnected)
    {
        using (await _mutex.LockAsync())
        {
            if (!_isConnected)
            {
                await _connection.ConnectAsync().ConfigureAwait(false);
                _isConnected = true;
            }
        }
    }
}

这是一种同步访问 initialise/open EventStore 连接的合理方法吗?

原来有一个有用的 nuget library and associated Github repo from Stephen Cleary 可以替代上面的 AsyncLock class。

我觉得你的做法是合理的。但是,如果您正在寻找管理初始化的异步方式,请查看 Microsoft.VisualStudio.Threading 包中的一些可用对象,例如 AsyncLazy。我不认为该包可用于 .NET 核心,但 source code 在 github 上并且在 MIT 许可下。

使用 AsyncLazy,你可以做这样的事情:

public class MyEventStoreConsumer
{
    private static readonly Func<Task<IEventStoreConnection>> getConnection;

    static MyEventStoreConsumer()
    {
        var eventStore = EventStoreConnection.Create(...);
        var connection = new AsyncLazy<IEventStoreConnection>(async () =>
        {
            await eventStore.ConnectAsync().ConfigureAwait(false);
            return eventStore;
        });
        getConnection = () => connection.GetValueAsync();
    }
}