Task.WhenAll 但一一处理结果

Task.WhenAll but Process results one by one

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

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.

提前致谢!

您可能正在寻找 Task.WhenAny

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

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

您可以改用 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.
}

您唯一需要注意的是:

The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.

Remarks in WhenAny Doks(本人强调)

如果您使用的是 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";
}

如果您想按任务完成的顺序处理结果,可以使用 OrderByCompletion extension method that does exactly that in Stephen Cleary's 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