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

有很多文章,但我似乎越读越困惑。我已经阅读并理解了Task库的基本原理,但我显然在某个地方遗漏了一个链接。

我知道可以将任务串联起来,这样它们就可以在另一个任务完成后开始(这几乎是我读过的所有文章的场景),但我希望所有任务同时运行,并且我想知道它们都完成后的情况。

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


当前回答

两个答案都没有提到可等待任务。WhenAll:

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

await Task.WhenAll(task1, task2);

任务之间的主要区别。WaitAll和Task。WhenAll表示前者将阻塞(类似于在单个任务上使用Wait),而后者不会阻塞,可以等待,将控制权交还给调用方,直到所有任务完成。

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

的任务。WaitAll:

至少一个Task实例被取消了,或者在执行至少一个Task实例期间抛出了异常。如果一个任务被取消了,AggregateException的InnerExceptions集合中包含一个OperationCanceledException。

的任务。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.

其他回答

这是我如何使用数组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

我所见过的最佳选择是以下扩展方法:

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

这样叫它:

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

或者使用async lambda:

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

应该有一个比公认的答案更简洁的解决方案。它不应该只用三步就能同时运行多个任务并获得结果。

创建任务 等待Task.WhenAll(任务) 获取任务结果(例如task1.Result)

这里有一个方法,可以把这个问题简化为两个步骤:

    public async Task<Tuple<T1, T2>> WhenAllGeneric<T1, T2>(Task<T1> task1, Task<T2> task2)
    {
        await Task.WhenAll(task1, task2);
        return Tuple.Create(task1.Result, task2.Result);
    }

你可以这样使用它:

var taskResults = await Task.WhenAll(DoWorkAsync(), DoMoreWorkAsync());
var DoWorkResult = taskResults.Result.Item1;
var DoMoreWorkResult = taskResults.Result.Item2;

这样就不需要临时任务变量了。使用它的问题是,虽然它适用于两个任务,但您需要为三个任务或任何其他数量的任务更新它。而且,如果其中一个任务没有返回任何东西,它也不能很好地工作。实际上,.Net库应该提供一些可以做到这一点的东西

两个答案都没有提到可等待任务。WhenAll:

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

await Task.WhenAll(task1, task2);

任务之间的主要区别。WaitAll和Task。WhenAll表示前者将阻塞(类似于在单个任务上使用Wait),而后者不会阻塞,可以等待,将控制权交还给调用方,直到所有任务完成。

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

的任务。WaitAll:

至少一个Task实例被取消了,或者在执行至少一个Task实例期间抛出了异常。如果一个任务被取消了,AggregateException的InnerExceptions集合中包含一个OperationCanceledException。

的任务。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.

如果你正在使用async/await模式,你可以像这样并行运行几个任务:

public async Task DoSeveralThings()
{
    // Start all the tasks
    Task first = DoFirstThingAsync();
    Task second = DoSecondThingAsync();

    // Then wait for them to complete
    var firstResult = await first;
    var secondResult = await second;
}