C# 需要一些异步调用的Singleton类

C# 需要一些异步调用的Singleton类,c#,asynchronous,C#,Asynchronous,我有一个Singleton类,它在构造时加载一些数据。问题是加载此数据需要调用async方法,但构造函数不能是async 换句话说,我的班级有以下结构: public class Singleton { private static Singleton instance; private Singleton() { LoadData(); } public static Singleton Instance { get

我有一个Singleton类,它在构造时加载一些数据。问题是加载此数据需要调用
async
方法,但构造函数不能是
async

换句话说,我的班级有以下结构:

public class Singleton
{
   private static Singleton instance;

   private Singleton() 
   {
       LoadData();
   }

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
       }
    }
}
LoadData()
是一个
async
函数,它调用许多
async
函数以及初始化。 如何正确调用
LoadData()
,以便所有内容都正确初始化

问题是加载此数据需要调用异步方法,但构造函数不能是异步的

虽然不能使构造函数本身异步,但可以从构造函数中调用异步方法。你不会马上得到结果

如果异步方法返回
Task
Task
,则在异步操作完成后,您始终可以使用任务的延续来设置类内的数据,或者根据场景中最有意义的内容,仅阻塞结果。如果不知道构建这个对象的需求,就很难知道在这个场景中什么是合适的


编辑:

考虑到上面列出的目标,一个选项是更改
单例
声明,以便检索
实例
的方法是一个方法,而不是一个属性。这将允许您使其异步:

public class Singleton
{
   private static Singleton instance;

   private Singleton() 
   {
          // Don't load the data here - will be called separately
   }

   public static async Task<Singleton> GetInstance()
   {
         if (instance == null)
         {
            instance = new Singleton();
            await instance.LoadData();
         }

         return instance;
    }
}
公共类单例
{
私有静态单例实例;
私人单身人士()
{
//不要在此处加载数据-将单独调用
}
公共静态异步任务GetInstance()
{
if(实例==null)
{
instance=newsingleton();
等待instance.LoadData();
}
返回实例;
}
}
这将允许您在调用时使用
wait
,以实际检索实例。这样做的好处是,它确实非常清楚您正在调用一个异步操作,并且您将得到对结果的正确处理,因为结果将像任何其他异步方法一样返回

但是,请注意,这不是线程安全的(尽管最初的版本也不是),因此如果要从多个线程中使用此单线程,可能需要重新考虑总体设计

另一个选项是使您的
Singleton
类不会自动加载数据。将从类中检索数据的方法改为异步。这提供了一些真正的优势,因为使用可能更加标准,并且您可以更轻松地支持来自多个线程的调用(因为您可以控制数据加载过程),而不是通过异步访问类实例来处理调用。

您可以使用:

public class Singleton
{
  private static readonly AsyncLazy<Singleton> instance =
      new AsyncLazy<Singleton>(CreateAndLoadData);

  private Singleton() 
  {
  }

  // This method could also be an async lambda passed to the AsyncLazy constructor.
  private static async Task<Singleton> CreateAndLoadData()
  {
    var ret = new Singleton();
    await ret.LoadDataAsync();
    return ret;
  }

  public static AsyncLazy<Singleton> Instance
  {
    get { return instance; }
  }
}

使用
AsyncLazy
的一个好处是它是线程安全的。但是,请注意,它总是在线程池线程上执行其委托。

好吧,异步初始化单例没有多大意义。如果只想调用在初始化时返回Task的方法,只需执行以下操作:

var task = MyAsyncMethod();
task.Wait();
return task.Result;
无需使方法
异步

但是,如果您希望singleton值成为一个任务,那么可以使用Lazy:

Lazy<Task<int>> l = new Lazy<Task<int>>(async () => { int i = await calculateNumber(); return i; });
Lazy l=new Lazy(异步()=>{int i=await calculateNumber();返回i;});

此外,
Lazy
是实现“单例”的首选方法。单例类很难正确(或很难保持正确)

线程安全、异步单例的解决方案实际上非常简单,如果我们只让
任务
类的内部机制为我们工作的话

那么,
任务
是如何工作的呢?假设您有一个
任务的实例,并且
等待它一次。现在,任务被执行,并生成一个
T
值并返回给您。如果您再次等待相同的任务实例,该怎么办?在这种情况下,任务只是以完全同步的方式立即返回先前生成的值

如果您同时从多个线程等待同一个任务实例(通常会得到竞争条件),该怎么办?第一个(因为将有一个首先到达那里)将执行任务代码,而其他人将等待处理结果。然后,当生成结果时,所有的
等待
将(实际上)同时完成并返回值

因此,线程安全的
异步
单例解决方案实际上非常简单:

public class Singleton
{
    private static readonly Task<Singleton> _getInstanceTask = CreateSingleton();

    public static Task<Singleton> Instance
    {
        get { return _getInstanceTask; }
    }

    private Singleton(SomeData someData)
    {
        SomeData = someData;
    }

    public SomeData SomeData { get; private set; }

    private static async Task<Singleton> CreateSingleton()
    {
        SomeData someData = await LoadData();
        return new Singleton(someData);
    }
}


请阅读此处的详细信息:

使用Lazy应该做您想做的事情。您也可以从构造函数调用异步方法。请提供有关您想做什么的更多上下文。目前还不太清楚。@PeterRitchie
Lazy
在这里有什么帮助?它不提供任何机制来等待异步操作…@ReedCopsey取决于他真正想要完成什么。我添加了一个详细的答案。如果您使用双重检查锁定,您将遇到一个问题,因为您不能使用wait-inside-of-lock()
public class Singleton
{
    private static readonly Task<Singleton> _getInstanceTask = CreateSingleton();

    public static Task<Singleton> Instance
    {
        get { return _getInstanceTask; }
    }

    private Singleton(SomeData someData)
    {
        SomeData = someData;
    }

    public SomeData SomeData { get; private set; }

    private static async Task<Singleton> CreateSingleton()
    {
        SomeData someData = await LoadData();
        return new Singleton(someData);
    }
}
Singleton mySingleton = await Singleton.Instance;
Singleton mySingleton = Singleton.Instance.Result;
SomeData mySingletonData = (await Singleton.Instance).SomeData;
SomeData mySingletonData = Singleton.Instance.Result.SomeData;