【问题标题】:Initialize Async Only Once Pattern只初始化一次异步模式
【发布时间】:2014-10-09 01:18:28
【问题描述】:

假设我有一个包含需要异步操作来初始化的成员的类(例如文件 i/o 或 Web 请求)。我只需要初始化一次,我不想重新初始化。

Tasks 和 Async-Await 是否适合完成此任务?

这是我目前正在做的一个例子:

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()。如果有多个依赖类,那么我不想加倍初始化它。

工厂方法? 通常不会构造多个需要初始化的实例,因此工厂方法似乎不是一个好的解决方案。我已经为文件夹包装类之类的东西使用了类似“静态 CreateAsync()”的方法,但这并没有让我将初始化的文件夹注入到构造函数中。当异步工厂方法不能与 IoC 构造函数注入一起使用时,它们不会获得任何收益。

【问题讨论】:

  • 你认为它有什么作用?
  • 您需要 Lazy<T> 来实现延迟加载行为。或静态构造函数,用于在类的每个实例中通用的初始化代码。
  • 你想什么时候开始初始化?施工中?
  • 如果它不是在施工中。您能否将类拆分为更小的类(组合单元),然后使用惰性方法开始对这些类进行初始化。
  • 看看这个:Async OOP 2: Constructors.

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


【解决方案1】:

您的代码可以工作,但它不是线程安全的,_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;
}

【讨论】:

  • 空合并运算符link 在空检查和赋值之间不是原子的吗?我想这希望太大了。这会创建一个具有潜在副作用的字段,不是吗?虽然它基本上是相同的代码被执行,但它现在在一个字段中,而不是一个降低代码清晰度的方法。 AsyncLazy 似乎确实是我想要的东西,有没有办法将它的实例化包含在方法中,以便更简洁?
  • 我更新了使用方法的答案,但是编译器要求方法是静态的。
  • 您可以使用Task.Run(taskFactory)代替使用StartNew()Unwrap()的方法。
【解决方案2】:

从常规初始化函数运行异步任务。在常规函数中,检查您的应用是否已经加载了符合您期望的数据集。如果数据不存在,则调用异步函数。

...
If (BooleanFunctionToDetermineIfDataIsNotPresent){FunctionToLoadFreshData}
...


Private Async Void FunctionToLoadFreshData{...}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2015-08-23
    • 1970-01-01
    • 2019-07-15
    相关资源
    最近更新 更多