Task.WhenAll 但一个一个处理结果

问题描述 投票:0回答:5

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

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# asynchronous async-await task task-parallel-library
5个回答
7
投票

您可能正在寻找Task.WhenAny.

我已经用它来启动一堆任务,然后在它们准备就绪时处理它们中的每一个,但我想你也可以等待一个完成并在没有循环的情况下继续,如果你不关心处理剩下的。

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

4
投票

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

3
投票

您可以使用 Task.WhenAny 代替。

来自 Stephen Cleary 的博客的“被盗”示例

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 设置为第一个要完成的任务。 即使要完成的第一个任务以已取消或故障状态结束也是如此。

WhenAny Doks中的备注(我强调)


1
投票

如果您想按任务完成的顺序处理结果,可以使用

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
}

如果您不喜欢外部依赖项,源代码在这里.


1
投票

根据Peter Csala的回答,这里是

IAsyncEnumerable
的扩展方法:

    public static async IAsyncEnumerable<T> OrderedByCompletion<T>(this IEnumerable<Task<T>> tasks)
    {
        List<Task<T>> taskList = new List<Task<T>>(tasks);

        while (taskList.Count > 0)
        {
            var finishedTask = await Task.WhenAny(taskList);
            taskList.Remove(finishedTask);
            yield return await finishedTask;
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.