【问题标题】:How to initilize an optional Task and Task<T> using C#?如何使用 C# 初始化可选的 Task 和 Task<T>?
【发布时间】:2019-12-13 20:47:28
【问题描述】:

如何选择性地创建多个任务并使用await Task.WhenAll(a,b,c);执行它们

我有以下课程

public class TaskProcessor
{
    private ProcessResult Result;

    public TaskProcessor(ProcessResult result)
    {
        Result = result ?? throw new ArgumentNullException(nameof(result));
    }

    public async Task<ProcessResult> Process(TaskOption option)
    {
        // First optional task
        Task<int?> typeFetcher = Task.FromResult<int?>(null);
        if (option is IProcessType t)
        {
            typeFetcher = t.GetAsync();
        }

        // Second optional task
        Task<int?> subtypeFetcher = Task.FromResult<int?>(null);
        if (option is IProcessSubtype st)
        {
            subtypeFetcher = st.GetAsync();
        }

        // Third optional task
        Task mainContent = Task.CompletedTask;

        if(Result.Content != null) 
        {
            mainContent = SetMainContentAsync(Result.Content);
        }

        await Task.WhenAll(typeFetcher, subtypeFetcher, mainContent);

        Result.TypeId = typeFetcher.Result,
        Result.SubtypeId = subtypeFetcher.Result;

        return result;
    }
}

我想将任务初始化为不执行任何操作的默认值。正如您在上面的代码中看到的,typeFetcher 任务使用Task.FromResult&lt;int?&gt;(null); 初始化,当给定的TaskOption 实现IProcessType 接口时,使用自定义任务覆盖该任务。 subtypeFetcher 任务的情况相同。

对于mainContent 任务,我使用Task.CompletedTask 对其进行了初始化,当Result.Content 不为空时,我将mainContent 设置为SetMainContentAsync(Result.Content)。最后,我使用await Task.WhenAll(typeFetcher, subtypeFetcher, mainContent) 等待所有任务。

ASP.NET Core Performance Best Practices 声明如下

不要:通过调用Task.WaitTask.Result来阻止异步执行。

由于我使用Task.FromResult&lt;int?&gt;(null); 来初始化任务,我觉得我违反了最佳实践。我想学习初始化可选任务的正确方法。

问题 初始化返回类型的可选任务的正确方法是什么?另外,初始化一个不返回任何东西到Task.CompletedTask 的任务是可以接受的还是有更正确的方法?

【问题讨论】:

  • 审阅说明:option is IProcessType t 只能在option 不是null 时为真,因此t != null 测试是多余的。
  • @madreflection 感谢您的提示!为了简单起见,我更新了代码
  • "由于我使用的是Task.FromResult(null);来初始化任务,我觉得我违反了最佳实践。我想学习初始化一个可选的正确方法任务。”是非常令人困惑的陈述...不确定它与以前的报价有什么关系...

标签: c# asynchronous async-await task


【解决方案1】:

你正在做的很好。 Task.FromResult 不是Task.Result,两者完全不相关。

Task.Result是一个实例属性,阻塞当前线程直到任务完成,并返回任务结果。

Task.FromResult 是一个静态方法,它返回一个微不足道的、已经完成的任务。

【讨论】:

    【解决方案2】:

    首先要做的事情:使用.Result 你现在的方式是“好的”,因为你已经通过使用await Task.WhenAll 确保它是完整的。您的代码不会在.Result 等待结果并阻塞线程。但是由于处理异常的方式,我在引号中说“好的”。当你使用.Result 时,异常总是放在AggregateException 中,你必须往里看才能找到真正的异常。

    但是,您仍然可以将await 用于已完成的任务,并且会按您的预期抛出异常:

    Result.TypeId = await typeFetcher;
    

    至于问题的其余部分:我发现等待多个可选任务的最佳方法是使用 List&lt;Task&gt; 并添加所需的任务。

    var taskList = new List<Task>();
    

    但是,由于您需要使用任务的返回值,因此这很复杂。有几种方法可以解决这个问题。您可以使用等待的列表,但也可以像以前一样保留对每个任务本身的引用。将Task 初始化为null 并仅在不是null 时设置该值。例如:

    var taskList = new List<Task>();
    
    Task<int?> typeFetcher = null;
    if (option is IProcessType t)
    {
        typeFetcher = t.GetAsync();
        taskList.Add(typeFetcher);
    }
    
    ...
    
    await Task.WhenAll(taskList);
    
    if (typeFetcher != null)
    {
        Result.TypeId = await typeFetcher;
    }
    
    ...
    

    但是你保留两个对同一事物的引用是......我不知道。似乎可以做得更好。

    另一个选项是使用 local functions 设置值并将其添加到您的任务列表中。这样,您不必在等待后设置值。

    public async Task<ProcessResult> Process(TaskOption option)
    {
        var taskList = new List<Task>();
    
        // First optional task
        if (option is IProcessType t)
        {
            // Declare a local function that sets the value
            async Task TypeFetcher()
            {
                Result.TypeId = await t.GetAsync();
            }
    
            // Call it and add the resulting Task to the list
            taskList.Add(TypeFetcher());
        }
    
        // Second optional task
        Task<int?> subtypeFetcher = Task.FromResult<int?>(null);
        if (option is IProcessSubtype st)
        {
            async Task SubtypeFetcher()
            {
                Result.SubtypeId = await st.GetAsync();
            }
            taskList.Add(SubtypeFetcher());
        }
    
        // Third optional task
        if(Result.Content != null) 
        {
            //don't need a local function here since there's no return value
            taskList.Add(SetMainContentAsync(Result.Content));
        }
    
        await Task.WhenAll(taskList);
    
        //I assume you actually declared and set result somewhere
        return result;
    }
    

    我并不是说这比为每个Task 保留两个引用更有效。只做你觉得最不容易混淆的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-23
      • 2015-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多