【问题标题】:Task.WhenAll but Process results one by oneTask.WhenAll but Process 结果一一对应
【发布时间】:2020-07-01 13:50:10
【问题描述】:

假设我有一个任务列表,我想并行运行它们。但我不需要所有这些都完成才能继续,我可以只用一个继续。以下代码等待所有任务完成继续。有没有办法让我在等待其他人完成的同时单独继续完成已完成的任务?

List<string>[] x = await Task.WhenAll(new Task<List<string>>[] { task1, task2 })
// when task1 finishes, I want to process the result immediately instead of waiting on task2.

提前致谢!

【问题讨论】:

标签: c# task task-parallel-library


【解决方案1】:

您可能正在寻找Task.WhenAny

我用它来启动一堆任务,然后在它们准备好时处理它们,但我想你也可以等待一个任务完成,如果你不关心,也可以不循环继续处理其余的。

while(tasks.Count() > 0)
{
    var task = await Task.WhenAny(tasks);
    tasks.Remove(task);
    var taskresult = await task;
    // process result
}

【讨论】:

    【解决方案2】:

    您可以改用Task.WhenAny

    来自Stephen Cleary's Blog 的“被盗”示例:

    var client = new HttpClient();
    string results = await await Task.WhenAny(
        client.GetStringAsync("http://example.com"),
        client.GetStringAsync("http://microsoft.com"));
    // results contains the HTML for whichever website responded first.
    

    回复评论

    您绝对可以跟踪其他任务:

    // supposing you have a list of Tasks in `myTasks`:
    while( myTasks.Count > 0 )
    {
        var finishedTask = await Task.WhenAny(myTasks);
        myTasks.Remove(finishedTask);
        handleFinishedTask(finishedTask); // assuming this is a method that
                                          // does the work on finished tasks.
    }
    

    您唯一需要注意的是:

    返回的任务将始终以 RanToCompletion 状态结束,其 Result 设置为第一个要完成的任务。 即使要完成的第一个任务以 Canceled 或 Faulted 状态结束也是如此。

    Remarks in WhenAny Doks(我强调)

    【讨论】:

    • 听起来不错,但我仍然想要那些没有先完成的人的回应。有什么办法吗?
    • 当然。如果您只使用一个await,您将获得已完成的任务并可以将其从列表中删除。就像理查德的回答一样。在 Stephen Cleary 的博客中还有更多示例。真的很值得一读,就像那个作者的任何东西一样。
    【解决方案3】:

    如果您使用的是 C# 8 和 .NET Core,则可以利用 IAsyncEnumerable 向消费方隐藏这种复杂性。

    就像这样:

    static async Task Main(string[] args)
    {
        await foreach (var data in GetData())
        {
            Console.WriteLine(data);
        }
    
        Console.ReadLine();
    }
    
    static async IAsyncEnumerable<string> GetData()
    {
        List<Task<string>> tasks = new List<Task<string>> {GetData1(), GetData3(), GetData2()};
    
        while (tasks.Any())
        {
            var finishedTask = await Task.WhenAny(tasks);
            tasks.Remove(finishedTask);
            yield return await finishedTask;
        }
    }
    
    static async Task<string> GetData1()
    {
        await Task.Delay(5000);
        return "Data1";
    }
    
    static async Task<string> GetData2()
    {
        await Task.Delay(3000);
        return "Data2";
    }
    
    static async Task<string> GetData3()
    {
        await Task.Delay(2000);
        return "Data3";
    }
    

    【讨论】:

      【解决方案4】:

      如果您想按任务完成的顺序处理结果,可以使用 OrderByCompletion 扩展方法,该方法在 Stephen Cleary 的 Nito.AsyncEx 库中完全一样,签名如下:

      // Creates a new collection of tasks that complete in order.
      public static List<Task<T>> OrderByCompletion<T>(this IEnumerable<Task<T>> @this);
      

      使用示例:

      Task<string>[] tasks = new[] { task1, task2, task3, task4 };
      foreach (var task in tasks.OrderByCompletion())
      {
          string result = await task;
          // Do something with result
      }
      

      如果你不喜欢有外部依赖,源代码是here

      【讨论】:

        猜你喜欢
        • 2014-06-04
        • 1970-01-01
        • 2018-12-17
        • 1970-01-01
        • 2020-08-22
        • 1970-01-01
        • 1970-01-01
        • 2016-07-26
        • 1970-01-01
        相关资源
        最近更新 更多