我有一个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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
LoadData()是一个async函数,它调用许多async函数以及初始化.如何LoadData()正确调用以便正确初始化所有内容?
Ree*_*sey 13
问题是加载这些数据需要调用异步方法,但构造函数不能是异步的.
虽然您不能使构造函数本身异步,但您可以从构造函数中调用异步方法.你不会马上得到结果.
如果异步方法返回Task或者Task<T>,您可以始终使用任务的延续来在异步操作完成后在类中设置数据,或者仅阻止结果,具体取决于您的方案中最有意义的内容.在不知道构造此对象的要求的情况下,很难知道在这种情况下什么是合适的.
编辑:
考虑到上面列出的目标,一个选项是更改您的Singleton声明,以便检索Instance方法而不是属性.这将允许您使其异步:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许您await在调用时使用实际检索实例.关于这一点的好处是它确实非常清楚你正在调用异步操作,并且你将得到正确的结果处理,因为结果将像任何其他异步方法一样返回.
但请注意,这不是线程安全的(虽然原始版本也不是),所以如果你要从多个线程使用这个Singleton,你可能不得不重新考虑整体设计.
另一种选择是让您的Singleton类不自动加载数据.而是使从类中检索数据的方法异步.这提供了一些真正的优势,因为使用可能更加标准,并且您可以更容易地支持来自多个线程的调用(因为您可以控制数据加载过程),而不是通过制作它来处理它异步访问类实例.
Mat*_*son 10
如果我们只让类的内部机制为我们工作,那么线程安全的异步单例的解决方案实际上非常简单Task!
那么,如何Task工作?假设您有一个a 的实例Task<T>而且await它有一次.现在执行任务,并生成一个值T并返回给您.如果再次await使用相同的任务实例怎么办?在这种情况下,任务只是以完全同步的方式立即返回先前生成的值.
如果您同时从多个线程(您通常会遇到竞争条件)await的同一个任务实例怎么办?那么,第一个(因为有会是一个抢先)将执行任务的代码,而其他人将等待处理结果.然后,当结果产生时,所有的将同时完成(虚拟)并返回值.await
因此async,线程安全的单例解决方案实际上非常简单:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以通过以下方式访问单例:
Singleton mySingleton = await Singleton.Instance;
Run Code Online (Sandbox Code Playgroud)
要么
Singleton mySingleton = Singleton.Instance.Result;
Run Code Online (Sandbox Code Playgroud)
要么
SomeData mySingletonData = (await Singleton.Instance).SomeData;
Run Code Online (Sandbox Code Playgroud)
要么
SomeData mySingletonData = Singleton.Instance.Result.SomeData;
Run Code Online (Sandbox Code Playgroud)
在这里阅读更多内容:异步单例初始化
您可以使用异步延迟初始化:
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; }
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
Singleton singleton = await Singleton.Instance;
Run Code Online (Sandbox Code Playgroud)
使用的一个好处AsyncLazy<T>是它是线程安全的.但是,请注意它始终在线程池线程上执行其委托.