【问题标题】:TaskCompletionSource for multiple objects awaiting same TaskTaskCompletionSource 用于等待相同任务的多个对象
【发布时间】:2021-09-28 03:42:40
【问题描述】:

我想知道在以下示例中使用TaskCompletionSource 类是否合适:

原始代码/不含TaskCompletionSource

public async Task GetData() 
{
    if (!IsLoaded) 
    {
        MyList = await Mediator.Send(new GetData());
        IsLoaded = true;
    }
}

此函数在我的 .NET Core Web 应用程序中作为单例范围服务的一部分提供。多个对象可以使用 MyList 中的记录,并在使用列表之前等待GetData() 函数。

您可以从示例函数中看到,当第一次调用仍在等待 Mediator 请求的结果时,可能会多次调用该方法。我想要一个允许多个对象等待同一个任务的解决方案。那么为此目的使用TaskCompletionSource 对象是否合适?

使用 TaskCompletionSource 重构

private TaskCompletionSource getDataTask;

public async Task VerifyDataIsLoaded() 
{
    if (getDataTask == null) 
    {
        getDataTask = new TaskCompletionSource();
        Task.Factory.StartNew(async () => 
        {
            await GetData();
            getDataTask.TrySetResult();
        }
    }
    return getDataTask.Task;
}

private async Task GetData() 
{
    MyList = await Mediator.Send(new GetData());
}

这将导致第一次调用VerifyDataIsLoaded() 实例化TaskCompletionSource getDataTask 对象的实例,在新的线程任务中启动GetData() 函数,并返回getDataTask.Task 属性。

现在,如果任何其他对象在 GetData() 函数等待从 Mediator 对象返回时调用 VerifyDataIsLoaded(),它们都将等待同一个任务,并防止对 GetData 的任何冗余调用。

想法?? ... 馊主意??更好的方法??

【问题讨论】:

  • 我认为您错过了一个非常重要的方面:如果GetData 失败了怎么办?因为你是在发火而忘记了时尚,所以如果它失败了,你永远不会知道,getDataTask 永远不会完成。
  • 可能这就是你想要的:Enforce an async method to be called once

标签: c# dependency-injection async-await task


【解决方案1】:

正常的模式是Lazy<Task<T>>,或custom AsyncLazy<T> type

private readonly Lazy<Task> getDataTask = new(GetData);
public Task VerifyDataIsLoadedAsync() => getDataTask.Value;

虽然一般来说,我不推荐这种异步初始化 API,因为消费者需要始终记住对其进行初始化。我更喜欢异步工厂模式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-04
    • 2020-08-05
    • 1970-01-01
    相关资源
    最近更新 更多