- 如果正在 await 一批任务,希望在每个任务完成时对它做一些处理。另外,希望在任务一完成就立即进行处理,而不需要等待其他任务。
按顺序 await 每个任务
举个例子,下面的代码启动了 3 个延时任务,然后对每一个进行 await。
static async Task<int> DelayAndReturnAsync(int val)
{
await Task.Delay(TimeSpan.FromSeconds(val));
return val;
}
// 当前,此方法输出 2 3 1
// 我们希望它输出 1 2 3
static async Task ProcessTasksAsync()
{
// 创建任务队列。
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
// 按顺序 await 每个任务。
foreach (var task in tasks)
{
var result = await task;
Trace.WriteLine(result);
}
}
按任务完成的次序进行处理,不必等待其他任务
- 虽然列表中的第二个任务是首先完成的,当前这段代码仍按列表的顺序对任务进行 await。
如果我们希望按任务完成的次序进行处理(例如 Trace.WriteLine),不必等待其他任务,可以考虑下面的方案:
方案一
static async Task AwaitAndProcessAsync(Task<int> task)
{
var result = await task;
Trace.WriteLine(result);
}
// 现在,这个方法输出 1 2 3
static async Task ProcessTasksAsync()
{
// 创建任务队列。
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
var processingTasks = (from t in tasks
select AwaitAndProcessAsync(t)).ToArray();
// 等待全部处理过程的完成。
await Task.WhenAll(processingTasks);
}
- 方案二:上面的代码也可以这么写:
static async Task ProcessTasksAsync()
{
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
var processingTasks = tasks.Select(async t =>
{
var result = await t;
Trace.WriteLine(result);
}).ToArray();
await Task.WhenAll(processingTasks);
}
- 重构后的代码是解决本问题较清晰、可移植性较好的方法。不过它与原始代码有一个细微的区别。重构后的代码并发地执行处理过程,而原始代码是一个接着一个地处理。