【问题标题】:AddSingleton with async Invoke?AddSingleton 与异步调用?
【发布时间】:2017-03-11 00:36:33
【问题描述】:

我像这样添加一个单例。

在 Startup.cs 中,我添加以下内容:

services.AddSingleton<MySingleton>();

我想像这样构建我的单例(当然这是不可能的):

public class MySingleton
{
    public MySingleton()
    {
        await InitAsync();
    }

    private async Task InitAsync()
    {
        await Stuff();
    }
}

如何在不产生死锁的情况下解决这个问题?

【问题讨论】:

    标签: c# asynchronous asp.net-core singleton .net-core


    【解决方案1】:

    据我所知,ASP.NET Core 中的配置不是异步的,因此无法使用异步工厂或类似的方式添加服务。

    正因为如此,考虑到如果在应用程序启动时阻塞不是一个大问题,我认为你应该在创建服务时阻塞。而且由于 ASP.NET Core 没有同步上下文,这也不会导致死锁。

    【讨论】:

    • 例如 Blazor 不允许阻塞
    【解决方案2】:

    使用Microsoft.VisualStudio.Threading NuGet 包,然后像这样设计您的类:

    public class Singleton {
    
      private static readonly AsyncLazy<Singleton> instance =
          new AsyncLazy<Singleton>( CreateAndDoSomething );
    
      private Singleton() {
      }
    
      // This method could also be an async lambda passed to the AsyncLazy constructor.
      private static async Task<Singleton> CreateAndDoSomething() {
         var ret = new Singleton();
         await ret.InitAsync();
         return ret;
      }
    
      private async Task InitAsync() {
         await Stuff();
      }
    
      public static AsyncLazy<Singleton> Instance
      {
         get { return instance; }
      }
    }
    

    用法:

    Singleton singleton = Singleton.Instance;
    

    更多关于Asynchronous Lazy Initialization

    【讨论】:

    • 这是否适用于来自 ASP.NET Core 依赖注入的AddSingleton?因为这就是问题所在。
    • 另外,Instance 返回 AsyncLazy&lt;Singleton&gt;,而不是 Singleton。您是否在使用时忘记了await
    【解决方案3】:

    看起来您面临的问题是构造函数不能具有异步修饰符。如果是这种情况,您可以执行以下操作。如果不是,请详细说明。

        public MySingleton()
        {
            InitAsync().Wait();
        }
    

    您可以探索的另一件事是尝试使用双重检查锁定模式 (DCLP)。如果会像下面 -

    private volatile object _sync = new Object();
    
    if (!_init) 
    {
        lock (_sync) 
        { 
            if (!_init) 
            { 
            }
         }
    }
    

    这在 C#/.Net 中受支持。您应该在互联网上找到更多关于此的参考资料。

    【讨论】:

    • DCLP 在这方面有何帮助?
    • 诚实,idk。但是,我只是把它扔在那里,原因如下。 1. DCLP 是初始化单例的常用模式。 2. 它为使用异步模式提供了更大的灵活性。 3. 在构造函数中做很多事情是不被接受的。即调用 InitAsync()。
    【解决方案4】:

    您可以正常使用构造它,并使用它异步并允许您的异步初始化在需要时延迟发生吗?

    public class MySingleton {
       private bool initialized=false;
       private SemaphoreSlim mutex = new SemaphoreSlim(1, 1);
       public async Task DoStuff() {
           if (!initialized) {
               await mutex.WaitAsync();
               try {
                   if (!initialized) {
                       await InitAsync();
                       initialized = true;
                   }
               } finally {
                   mutex.Release();
               }
           }
           DoTheRealStuff();
       }
       private async Task InitAsync() {
           await Stuff();
       }
    }
    

    【讨论】:

    • 你不能在lock 中使用await
    • @svick,你完全正确,我忘了。我更新了我的示例,改为使用 SemaphoreSlim 作为互斥体。
    猜你喜欢
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    相关资源
    最近更新 更多