×

C#异步编程: 任务并发与组合(WhenAll)

独孤求败 独孤求败 发表于2026-05-08 14:04:16 浏览34 评论0

抢沙发发表评论

这一章解决一个完整问题:

如何正确并发执行多个任务,并统一等待它们完成

本章包含 三个紧密关联的知识点

1 Task什么时候开始执行
2 并发启动任务的正确方式
3 Task.WhenAll任务组合

这三个知识点是一条完整逻辑链:

任务什么时候启动
      ↓
如何同时启动多个任务
      ↓
如何优雅等待它们完成

任务并发与组合

在真实工程中非常常见的需求:

同时调用多个异步操作
等它们全部完成

例如:

加载用户信息
加载订单
加载余额

如果写不好,会导致:

本来100ms
变成300ms

Task什么时候开始执行

很多初学者误解:

await 才会启动任务

实际上:

调用 async 方法时任务就开始了

看代码:

Task t = Task.Delay(2000);

执行流程:

创建Task

定时器开始

2秒后Task完成

注意:

await 不会启动任务
await 只是等待任务

示例

async Task Test()
{
    Console.WriteLine("A");

    var t = Task.Delay(2000);

    Console.WriteLine("B");

    await t;

    Console.WriteLine("C");
}

执行顺序:

A
B
(2秒)
C

说明:

Task在创建时就开始

如何并发启动任务

现在看一个常见错误写法:

async Task Test()
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}

执行时间:

≈ 3秒

执行模型:

Delay1

Delay2

Delay3

这是 串行执行


正确并发方式

async Task Test()
{
    var t1 = Task.Delay(1000);
    var t2 = Task.Delay(1000);
    var t3 = Task.Delay(1000);

    await t1;
    await t2;
    await t3;
}

执行模型:

t1 ┐
t2 ├ 同时开始
t3 ┘

执行时间:≈1秒

原因:任务先启动,await只是等待


Task.WhenAll 任务组合

上面的写法虽然能并发,但代码不优雅:

await t1;
await t2;
await t3;

.NET提供:Task.WhenAll ,  作用:等待所有任务完成


示例

async Task Test()
{
    var t1 = Task.Delay(2000);
    var t2 = Task.Delay(3000);
    var t3 = Task.Delay(1000);

    await Task.WhenAll(t1, t2, t3);

    Console.WriteLine("全部完成");
}

执行时间:≈3秒,因为:t2 最慢


带返回值

Task<int> t1 = GetData(1);
Task<int> t2 = GetData(2);
Task<int> t3 = GetData(3);

int[] results = await Task.WhenAll(t1, t2, t3);

返回:

results[0] -> t1
results[1] -> t2
results[2] -> t3

一个真实工程例子

错误写法:

var user = await GetUser();
var order = await GetOrders();
var balance = await GetBalance();

时间:

100ms + 100ms + 100ms = 300ms

优化:

var userTask = GetUser();
var orderTask = GetOrders();
var balanceTask = GetBalance();

await Task.WhenAll(userTask, orderTask, balanceTask);

时间:≈100ms ,这是 API聚合服务的标准写法


本章核心模型

这一章其实建立了一个重要的 async 模式:

启动任务

并发执行

统一等待

代码模板:

var t1 = Task1();
var t2 = Task2();
var t3 = Task3();

await Task.WhenAll(t1, t2, t3);

这是 服务器代码中非常常见的结构

练习1

代码:

async Task Test()
{
    var t1 = Task.Delay(2000);

    await Task.Delay(2000);

    await t1;
}

问题:总时间是多少?为什么?

执行时间线

t=0
创建 t1 (2秒)

t=0
await Task.Delay(2000)

t=2
t1 已完成
Delay 完成

await t1
立即返回

总时间:≈2秒


练习2

代码:

async Task Test()
{
    var t1 = Task.Delay(2000);
    var t2 = Task.Delay(4000);

    await t1;

    Console.WriteLine("A");

    await t2;

    Console.WriteLine("B");
}

问题:

A什么时候打印
B什么时候打印

时间线

t=0
t1开始 (2s)
t2开始 (4s)

t=2
t1完成
打印 A

t=4
t2完成
打印 B

注意关键点:t2 在等待 A 的时候已经执行了 2 秒


练习3(理解并发)

判断下面代码是否真正并发:

await Task.WhenAll(
    GetData1(),
    GetData2(),
    GetData3()
);

问题:任务什么时候开始执行?

执行过程:

调用 GetData1() → Task1开始
调用 GetData2() → Task2开始
调用 GetData3() → Task3开始

然后:WhenAll 等待

所以:并发发生在调用方法时,不是WhenAll


群贤毕至

访客