让我们说我有一个类,其成员需要异步操作来初始化(例如文件i / o或web请求)。我只需要初始化一次,但我不想重新初始化。
任务和异步 - 是否适合完成此任务?
以下是我目前正在做的事情的一个例子:
private Task _initializeTask;
public Task InitializeAsync()
{
return _initializeTask ?? (_initializeTask = Task.Run(
async () =>
{
// Do an action requiring await here
await _storageField.LoadAsync();
}));
}
这是否符合我的想法?有更好的方法吗?
线程安全吗?不是要求,但应予以考虑。
编辑:
我认为它的作用是什么?我相信如果_initializeTask没有被分配,那么它将被分配一个新的任务,它将启动,然后等待其中包含的异步lambda。对该方法的任何后续调用都将等待已分配给_initializedTask的已运行(或已完成)任务。
我什么时候需要构建它?通常我会在使用IoC容器解析的服务上使用这种方法。可以使用对类的引用来构造多个依赖类。然后,在使用之前,每个都等待InitializeAsync()。如果有多个依赖类,那么我不想在初始化时加倍。
工厂方法?通常不会构造需要初始化的多个实例,因此Factory方法似乎不是一个好的解决方案。我使用了类似"静态CreateAsync()"像文件夹包装类这样的方法,但是我没有让初始化文件夹注入构造函数。异步工厂方法在无法与IoC构造函数注入一起使用时,无法获得任何收益。
答案 0 :(得分:5)
您的代码可以正常运行,但它不是线程安全的,_initializeTask
可以在检查null
之后和初始化之前进行更改。这将导致两次初始化。我会考虑使用AsyncLazy<T>
,它继承自Lazy<T>
,这是线程安全的。
然后假设LoadAsync
返回Task
而不是Task<T>
,您的代码将变为(未经测试):
private AsyncLazy<object> initializeTask = new AsyncLazy<object>(async () =>
{
// Do an action requiring await here
await _storageField.LoadAsync();
return null;
});
public Task InitializeAsync()
{
return _initializeTask.Value;
}
您还可以定义非泛型版本的`AsyncLazy,因此您不必从初始化例程返回值。
public class AsyncLazy : Lazy<Task>
{
public AsyncLazy(Func<Task> taskFactory) :
base(() => Task.Run(taskFactory)) { }
}
然后您可以使用初始化方法对其进行初始化,但编译器需要该方法是静态的:
private AsyncLazy _initializeTask = new AsyncLazy(LoadStorageAsync);
private static async Task LoadStorageAsync()
{
// Do an action requiring await here
await _storageField.LoadAsync();
}
public Task InitializeAsync()
{
return _initializeTask.Value;
}
答案 1 :(得分:0)
从常规初始化函数运行异步任务。在常规功能中,检查您的应用是否已经加载了符合您预期的数据集。如果数据不存在,则调用异步函数。
...
If (BooleanFunctionToDetermineIfDataIsNotPresent){FunctionToLoadFreshData}
...
Private Async Void FunctionToLoadFreshData{...}
加载数据的函数不能返回值,以免它成为任务本身。