在C#异步编程场景中,经常需要同时执行多个独立的异步操作,比如同时调用多个外部接口获取数据、并行处理多个文件读写任务等。如果逐个等待这些异步任务完成,总耗时将是所有任务耗时的总和,而使用Task.WhenAll方法可以让所有任务并行执行,仅等待最慢的那个任务完成,极大提升程序运行效率。

Task.WhenAll基本用法
Task.WhenAll是System.Threading.Tasks命名空间下的静态方法,它接收一个任务集合作为参数,返回一个代表所有任务完成的新任务。当传入的所有任务都完成时,返回的任务才会进入完成状态。
等待无返回值的多个异步任务
如果多个异步任务都没有返回值,可以直接将任务集合传入Task.WhenAll,等待所有任务执行完毕即可。
using System;
using System.Threading.Tasks;
class Program
{
// 模拟无返回值的异步任务
static async Task AsyncTask1()
{
await Task.Delay(1000); // 模拟耗时1秒的操作
Console.WriteLine("任务1执行完成");
}
static async Task AsyncTask2()
{
await Task.Delay(1500); // 模拟耗时1.5秒的操作
Console.WriteLine("任务2执行完成");
}
static async Task AsyncTask3()
{
await Task.Delay(800); // 模拟耗时0.8秒的操作
Console.WriteLine("任务3执行完成");
}
static async Task Main(string[] args)
{
// 创建三个异步任务
Task task1 = AsyncTask1();
Task task2 = AsyncTask2();
Task task3 = AsyncTask3();
// 并行等待所有任务完成
await Task.WhenAll(task1, task2, task3);
Console.WriteLine("所有无返回值任务都执行完毕");
}
}
上述代码中三个任务并行执行,总耗时约为最慢的任务耗时1.5秒,而不是三个任务耗时相加的3.3秒。
等待有返回值的多个异步任务
如果异步任务有返回值,Task.WhenAll会将所有任务的返回值收集到一个数组中,按照传入任务的顺序返回结果。
using System;
using System.Threading.Tasks;
class Program
{
// 模拟有返回值的异步任务
static async Task<int> GetDataAsync1()
{
await Task.Delay(1000);
return 100;
}
static async Task<int> GetDataAsync2()
{
await Task.Delay(1200);
return 200;
}
static async Task<int> GetDataAsync3()
{
await Task.Delay(900);
return 300;
}
static async Task Main(string[] args)
{
// 创建三个有返回值的异步任务
Task<int> task1 = GetDataAsync1();
Task<int> task2 = GetDataAsync2();
Task<int> task3 = GetDataAsync3();
// 等待所有任务完成并获取结果
int[] results = await Task.WhenAll(task1, task2, task3);
Console.WriteLine($"所有任务结果:{string.Join(",", results)}"); // 输出 100,200,300
}
}
Task.WhenAll的异常处理
当使用Task.WhenAll等待多个任务时,如果其中某个任务抛出异常,返回的任务会进入故障状态,异常会被包装在AggregateException中。需要注意,只有所有任务都完成后,异常才会被抛出,不会因为某个任务异常就立即中断等待。
using System;
using System.Threading.Tasks;
class Program
{
static async Task TaskWithException()
{
await Task.Delay(500);
throw new InvalidOperationException("任务执行出错");
}
static async Task NormalTask()
{
await Task.Delay(1000);
Console.WriteLine("正常任务执行完成");
}
static async Task Main(string[] args)
{
Task task1 = TaskWithException();
Task task2 = NormalTask();
try
{
await Task.WhenAll(task1, task2);
}
catch (AggregateException ex)
{
// 遍历所有内部异常
foreach (var innerEx in ex.InnerExceptions)
{
Console.WriteLine($"捕获到异常:{innerEx.Message}");
}
}
catch (Exception ex)
{
// 如果只传入一个任务,可能直接抛出对应异常
Console.WriteLine($"其他异常:{ex.Message}");
}
}
}
上述代码中,即使第一个任务抛出异常,程序还是会等待第二个任务执行完成,之后才会进入异常处理逻辑,不会提前中断等待。
使用注意事项
- 传入Task.WhenAll的任务集合不能为空,否则会抛出
ArgumentException异常。 - 不要在启动任务之前就调用Task.WhenAll,否则会立即返回一个已经完成的任务,无法达到并行等待的效果。
- 如果多个任务之间存在依赖关系,不适合使用Task.WhenAll,应该按照依赖顺序逐个执行。
- 当任务数量非常多时,需要考虑系统的资源承载能力,避免同时启动过多任务导致资源耗尽。
适用场景总结
Task.WhenAll适合所有任务相互独立、没有依赖关系、可以并行执行的场景,比如:
- 同时调用多个第三方接口获取数据,最后汇总结果。
- 并行处理多个独立的文件、图片处理任务。
- 同时向多个消息队列发送消息,等待所有发送完成。
合理使用Task.WhenAll可以有效提升C#异步程序的执行效率,减少不必要的等待时间,是进阶异步编程必须掌握的核心方法。
C#TaskWhenAll异步任务并行等待修改时间:2026-07-24 03:24:30