仅初始化一次异步模式

时间:2014-10-09 01:18:28

标签: c# asynchronous task-parallel-library async-await task

让我们说我有一个类,其成员需要异步操作来初始化(例如文件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构造函数注入一起使用时,无法获得任何收益。

2 个答案:

答案 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{...}

加载数据的函数不能返回值,以免它成为任务本身。