强制执行一次异步方法

tal*_*als 13 .net c# asynchronous task-parallel-library async-await

假设我有一个需要使用InitializeAsync()方法执行异步初始化的类.我想确保初始化只执行一次.如果另一个线程在初始化正在进行时调用此方法,它将"等待"直到第一个调用返回.

我正在考虑以下的实现(使用SemaphoreSlim).有更好/更简单的方法吗?

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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

谢谢!

编辑:

由于我正在使用DI并且将注入此服务,因此将其作为"懒惰"资源使用或使用异步工厂对我来说不起作用(尽管在其他用例中它可能很好).因此,异步初始化应该封装在类中,并对IMyService消费者透明.

将初始化代码包装在"虚拟" AsyncLazy<>对象中的想法将完成这项工作,尽管对我来说感觉有点不自然.

Yuv*_*kov 9

我会去AsyncLazy<T>(略有修改版):

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(); } 
}
Run Code Online (Sandbox Code Playgroud)

并像这样消耗它:

private AsyncLazy<bool> asyncLazy = new AsyncLazy<bool>(async () =>
                                    { 
                                        await DoStuffOnlyOnceAsync()
                                        return true;
                                    });
Run Code Online (Sandbox Code Playgroud)

注意我bool只是因为你没有返回类型而使用DoStuffOnlyOnceAsync.

编辑:

斯蒂芬·克利里(当然)也有这方面的一个实现.


i3a*_*non 7

是.使用Stephen ClearyAsyncLazy(可在AsyncExnuget获得):

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;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

视觉工作室SDK的AsyncLazy,如果你喜欢微软的实现.


Ste*_*ary 5

我有一篇博文,介绍了做"异步构造函数"的几个不同选项.

通常,我更喜欢异步工厂方法,因为我认为它们更简单,更安全:

public class MyService
{
  private MyService() { }

  public static async Task<MyService> CreateAsync()
  {
    var result = new MyService();
    result.Value = await ...;
    return result;
  }
}
Run Code Online (Sandbox Code Playgroud)

AsyncLazy<T>是定义共享异步资源的一种非常好的方法(并且可能是"服务"的更好的概念匹配,具体取决于它的使用方式).异步工厂方法方法的一个优点是无法创建未初始化的版本MyService.