运行多个异步任务并等待它们全部完成

时间:2021-05-28 20:54:21

I need to run multiple async tasks in a console application, and wait for them all to complete before further processing.

我需要在控制台应用程序中运行多个异步任务,并在进一步处理之前等待它们全部完成。

There's many articles out there, but I seem to get more confused the more I read. I've read and understand the basic principles of the Task library, but I'm clearly missing a link somewhere.

外面有很多文章,但我读得越多,就越糊涂。我已经阅读并理解了任务库的基本原理,但是我显然在某个地方漏掉了一个链接。

I understand that it's possible to chain tasks so that they start after another completes (which is pretty much the scenario for all the articles I've read), but I want all my Tasks running at the same time, and I want to know once they're all completed.

我知道有可能将任务链化,以便它们在另一个完成之后开始(这几乎是我读过的所有文章的场景),但我希望所有任务同时运行,并且我想知道它们是否都完成了。

What's the simplest implementation for a scenario like this?

对于这样的场景,最简单的实现是什么?

6 个解决方案

#1


281  

Both answers didn't mention the awaitable Task.WhenAll:

两个回答都没有提到可爱的任务。

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

The main difference between Task.WaitAll and Task.WhenAll is that the former will block (similar to using Wait on a single task) while the latter will not and can be awaited, yielding control back to the caller until all tasks finish.

任务之间的主要区别。WaitAll和任务。当所有情况都发生时,前者将会阻塞(类似于在单个任务上使用Wait),而后者将不会并且可以等待,从而将控制权交还给调用者,直到所有任务完成。

More so, exception handling differs:

更重要的是,异常处理不同:

Task.WaitAll:

Task.WaitAll:

At least one of the Task instances was canceled -or- an exception was thrown during the execution of at least one of the Task instances. If a task was canceled, the AggregateException contains an OperationCanceledException in its InnerExceptions collection.

至少有一个任务实例被取消——在至少一个任务实例的执行过程中抛出一个异常。如果一个任务被取消,AggregateException在其innerexception集合中包含一个OperationCanceledException。

Task.WhenAll:

Task.WhenAll:

If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.

如果任何一个提供的任务在一个故障状态中完成,那么返回的任务也将在一个错误的状态中完成,在这个状态中,它的异常将包含来自每个提供的任务的未包装异常集的集合。

If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.

如果没有提供的任务出错,但至少有一个任务被取消,则返回的任务将以取消状态结束。

If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion state before it's returned to the caller.

如果没有一个任务出错,并且没有一个任务被取消,那么结果任务将以RanToCompletion状态结束。如果所提供的数组/enumerable不包含任务,则返回的任务在返回给调用者之前将立即转换为RanToCompletion状态。

#2


82  

You could create many tasks like:

您可以创建许多任务,比如:

List<Task> TaskList = new List<Task>();
foreach(...)
{
   var LastTask = new Task(SomeFunction);
   LastTask.Start();
   TaskList.Add(LastTask);
}

Task.WaitAll(TaskList.ToArray());

#3


19  

The best option I've seen is the following extension method:

我看到的最好的选择是下面的扩展方法:

public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
    return Task.WhenAll(sequence.Select(action));
}

Call it like this:

叫它是这样的:

await sequence.ForEachAsync(item => item.SomethingAsync(blah));

Or with an async lambda:

或者用异步lambda:

await sequence.ForEachAsync(async item => {
    var more = await GetMoreAsync(item);
    await more.FrobbleAsync();
});

#4


9  

Do you want to chain the Tasks, or can they be invoked in a parallel manner?

您希望将任务链化,还是可以以并行方式调用它们?

For chaining
Just do something like

链接可以做一些类似的事情

Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);

and don't forget to check the previous Task instance in each ContinueWith as it might be faulted.

不要忘记在每个ContinueWith中检查前一个任务实例,因为它可能会出错。

For the parallel manner
The most simple method I came across: Parallel.Invoke Otherwise there's Task.WaitAll or you can even use WaitHandles for doing a countdown to zero actions left (wait, there's a new class: CountdownEvent), or ...

对于并行方式,我遇到的最简单的方法是:并行。调用其他的任务。WaitAll或者你甚至可以使用WaitHandles来倒数剩下的0动作(等等,有一个新类:CountdownEvent),或者……

#5


6  

You can use WhenAll which will return an awaitable Task or WaitAll which has no return type and will block further code execution simular to Thread.Sleep until all tasks are completed, canceled or faulted.

您可以使用WhenAll,它将返回一个可爱的任务或WaitAll,它没有返回类型,并将阻塞进一步的代码执行。一直睡到所有的任务完成,取消或出错。

运行多个异步任务并等待它们全部完成

Example

例子

var tasks = new Task[] {
    await TaskOperationOne(),
    await TaskOperationTwo()
};

Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);

If you want to run the tasks in a praticular order you can get inspiration form this anwser.

如果你想按常规的顺序完成任务,你可以从这个列表中获得灵感。

#6


3  

This is how I do it with an array Func<>:

这就是我如何使用数组Func<>:

var tasks = new Func<Task>[]
{
   () => myAsyncWork1(),
   () => myAsyncWork2(),
   () => myAsyncWork3()
};

await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async    
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync

#1


281  

Both answers didn't mention the awaitable Task.WhenAll:

两个回答都没有提到可爱的任务。

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

The main difference between Task.WaitAll and Task.WhenAll is that the former will block (similar to using Wait on a single task) while the latter will not and can be awaited, yielding control back to the caller until all tasks finish.

任务之间的主要区别。WaitAll和任务。当所有情况都发生时,前者将会阻塞(类似于在单个任务上使用Wait),而后者将不会并且可以等待,从而将控制权交还给调用者,直到所有任务完成。

More so, exception handling differs:

更重要的是,异常处理不同:

Task.WaitAll:

Task.WaitAll:

At least one of the Task instances was canceled -or- an exception was thrown during the execution of at least one of the Task instances. If a task was canceled, the AggregateException contains an OperationCanceledException in its InnerExceptions collection.

至少有一个任务实例被取消——在至少一个任务实例的执行过程中抛出一个异常。如果一个任务被取消,AggregateException在其innerexception集合中包含一个OperationCanceledException。

Task.WhenAll:

Task.WhenAll:

If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.

如果任何一个提供的任务在一个故障状态中完成,那么返回的任务也将在一个错误的状态中完成,在这个状态中,它的异常将包含来自每个提供的任务的未包装异常集的集合。

If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.

如果没有提供的任务出错,但至少有一个任务被取消,则返回的任务将以取消状态结束。

If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion state before it's returned to the caller.

如果没有一个任务出错,并且没有一个任务被取消,那么结果任务将以RanToCompletion状态结束。如果所提供的数组/enumerable不包含任务,则返回的任务在返回给调用者之前将立即转换为RanToCompletion状态。

#2


82  

You could create many tasks like:

您可以创建许多任务,比如:

List<Task> TaskList = new List<Task>();
foreach(...)
{
   var LastTask = new Task(SomeFunction);
   LastTask.Start();
   TaskList.Add(LastTask);
}

Task.WaitAll(TaskList.ToArray());

#3


19  

The best option I've seen is the following extension method:

我看到的最好的选择是下面的扩展方法:

public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
    return Task.WhenAll(sequence.Select(action));
}

Call it like this:

叫它是这样的:

await sequence.ForEachAsync(item => item.SomethingAsync(blah));

Or with an async lambda:

或者用异步lambda:

await sequence.ForEachAsync(async item => {
    var more = await GetMoreAsync(item);
    await more.FrobbleAsync();
});

#4


9  

Do you want to chain the Tasks, or can they be invoked in a parallel manner?

您希望将任务链化,还是可以以并行方式调用它们?

For chaining
Just do something like

链接可以做一些类似的事情

Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);

and don't forget to check the previous Task instance in each ContinueWith as it might be faulted.

不要忘记在每个ContinueWith中检查前一个任务实例,因为它可能会出错。

For the parallel manner
The most simple method I came across: Parallel.Invoke Otherwise there's Task.WaitAll or you can even use WaitHandles for doing a countdown to zero actions left (wait, there's a new class: CountdownEvent), or ...

对于并行方式,我遇到的最简单的方法是:并行。调用其他的任务。WaitAll或者你甚至可以使用WaitHandles来倒数剩下的0动作(等等,有一个新类:CountdownEvent),或者……

#5


6  

You can use WhenAll which will return an awaitable Task or WaitAll which has no return type and will block further code execution simular to Thread.Sleep until all tasks are completed, canceled or faulted.

您可以使用WhenAll,它将返回一个可爱的任务或WaitAll,它没有返回类型,并将阻塞进一步的代码执行。一直睡到所有的任务完成,取消或出错。

运行多个异步任务并等待它们全部完成

Example

例子

var tasks = new Task[] {
    await TaskOperationOne(),
    await TaskOperationTwo()
};

Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);

If you want to run the tasks in a praticular order you can get inspiration form this anwser.

如果你想按常规的顺序完成任务,你可以从这个列表中获得灵感。

#6


3  

This is how I do it with an array Func<>:

这就是我如何使用数组Func<>:

var tasks = new Func<Task>[]
{
   () => myAsyncWork1(),
   () => myAsyncWork2(),
   () => myAsyncWork3()
};

await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async    
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync