我需要在控制台应用程序运行多个异步任务,并等待他们的所有进一步的处理之前完成。
有很多文章在那里,但我似乎得到更多的困惑我越读。 我已阅读并理解任务库的基本原则,但我清楚地缺少的环节上。
据我所知,这是可能的连锁任务,使他们另一个完成之后启动(这是几乎所有我读过的文章的情况下),但我想我所有的在同一时间运行的任务,我想知道,一旦他们都完成了。
什么是像这样的情况下最简单的实现?
我需要在控制台应用程序运行多个异步任务,并等待他们的所有进一步的处理之前完成。
有很多文章在那里,但我似乎得到更多的困惑我越读。 我已阅读并理解任务库的基本原则,但我清楚地缺少的环节上。
据我所知,这是可能的连锁任务,使他们另一个完成之后启动(这是几乎所有我读过的文章的情况下),但我想我所有的在同一时间运行的任务,我想知道,一旦他们都完成了。
什么是像这样的情况下最简单的实现?
这两个答案没有提及awaitable Task.WhenAll
:
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
之间的主要区别Task.WaitAll
和Task.WhenAll
在于,前者将阻止(类似于使用Wait
上一个任务),而后者不会,也被期待已久,产生控制权返回给调用者,直到所有任务完成。
更何况,异常处理的区别:
Task.WaitAll
:
至少任务实例之一被取消了 - 或者一个异常被任务实例中的至少一个的执行过程中抛出。 如果任务被取消,该AggregateException包含在其InnerExceptions收集的OperationCanceledException。
Task.WhenAll
:
如果有任何的供应任务故障状态结束,返回的任务也将完成在故障状态,其中它的异常将包含一套从每个供应任务展开异常的聚集。
如果没有提供的任务故障,但其中至少有一个被取消,返回的任务将在取消状态结束。
如果没有任务故障,没有任务被取消,所产生的任务将在RanToCompletion状态结束。 如果所提供的阵列/枚举不包含任何任务,之前它返回给调用者返回的任务将立即转换到一个RanToCompletion状态。
你可以创建许多任务,如:
List<Task> TaskList = new List<Task>();
foreach(...)
{
var LastTask = new Task(SomeFunction);
LastTask.Start();
TaskList.Add(LastTask);
}
Task.WaitAll(TaskList.ToArray());
我见过的最好的选择是下面的扩展方法:
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));
或与异步拉姆达:
await sequence.ForEachAsync(async item => {
var more = await GetMoreAsync(item);
await more.FrobbleAsync();
});
您可以使用WhenAll
它会返回一个awaitable Task
或WaitAll
它没有返回类型,并会阻止进一步的代码执行simular到Thread.Sleep
直到所有的任务都完成,取消或故障。
例
var tasks = new Task[] {
TaskOperationOne(),
TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
如果你想在praticular顺序运行任务,就可以得到灵感的形式这 anwser。
你想链中的Task
S,或者他们能以并行的方式来调用?
对于链接
只是这样做
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
不要忘记检查前面的Task
实例中的每个ContinueWith
,因为它可能会出现故障。
对于平行的方式
最简单的方法,我碰到: Parallel.Invoke
否则有Task.WaitAll
或者你甚至可以使用WaitHandle
S代表做了倒计时左零个操作(请等一下,还有一个新的类: CountdownEvent
),或...
这是我与阵列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
然而,另一种答案......但我通常发现自己在一个情况下,我需要同时读取数据,然后把它放到变量,如:
var cats = new List<Cat>();
var dog = new Dog();
var loadDataTasks = new Task[]
{
Task.Run(async () => cats = await LoadCatsAsync()),
Task.Run(async () => dog = await LoadDogAsync())
};
try
{
await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
// handle exception
}
我准备了一段代码向你展示了如何使用任务其中一些场景。
// method to run tasks in a parallel
public async Task RunMultipleTaskParallel(Task[] tasks) {
await Task.WhenAll(tasks);
}
// methode to run task one by one
public async Task RunMultipleTaskOneByOne(Task[] tasks)
{
for (int i = 0; i < tasks.Length - 1; i++)
await tasks[i];
}
// method to run i task in parallel
public async Task RunMultipleTaskParallel(Task[] tasks, int i)
{
var countTask = tasks.Length;
var remainTasks = 0;
do
{
int toTake = (countTask < i) ? countTask : i;
var limitedTasks = tasks.Skip(remainTasks)
.Take(toTake);
remainTasks += toTake;
await RunMultipleTaskParallel(limitedTasks.ToArray());
} while (remainTasks < countTask);
}