C# Task 进阶:WaitAll / WaitAny / WhenAll / WhenAny

一、Task 同步阻塞:WaitAll 与 WaitAny

1. 核心原理

WaitAll、WaitAny 属于同步阻塞方法,执行后会阻塞当前主线程,等待任务集合满足条件后,才继续执行后续代码。

  • Task.WaitAll() :阻塞线程,等待集合中所有任务全部执行完毕,才放行后续代码

  • Task.WaitAny() :阻塞线程,只要集合中任意一个任务执行完毕,立刻放行后续代码

案例中设置无限循环任务,用于直观测试:单个任务完成触发 WaitAny、永久无法全部完成触发 WaitAll 阻塞。

2. 独立完整代码

cs 复制代码
private void button1_Click(object sender, EventArgs e)
{
    TaskFactory factory = new TaskFactory();
    List<Task> taskList = new List<Task>();

    // 添加三个异步任务,第三个任务无限循环永不结束
    taskList.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
    taskList.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
    taskList.Add(factory.StartNew(() => { while (true) { } Console.WriteLine("查询接口"); }));

    // 同步阻塞:任意一个任务完成即放行
    Task.WaitAny(taskList.ToArray());

    // 所有任务满足条件后执行
    Console.WriteLine("至少一个操作执行完毕,主线程放行");
}

3. 注意事项

  • WaitAll、WaitAny 为同步方法,会直接阻塞UI主线程,造成窗体卡死,WinForm 项目慎用

  • 存在永久执行的任务时,WaitAll 会永久死锁阻塞,永远无法放行

  • WaitAny 只判断首个完成的任务,不关心剩余任务执行状态

  • 适用于控制台程序同步等待,不推荐窗体UI线程直接调用


二、Task 异步回调:WhenAll 与 WhenAny

1. 核心原理

WhenAll、WhenAny 属于异步非阻塞方法 ,不会阻塞主线程,主线程代码会直接向下执行。需搭配 ContinueWith 实现任务完成后的回调逻辑。

  • Task.WhenAll():所有任务执行完毕后,触发回调方法

  • Task.WhenAny():任意一个任务执行完毕后,立刻触发回调方法

功能和 WaitAll/WaitAny 一致,核心区别为异步不阻塞UI,是项目开发主流用法。

2. 独立完整代码

cs 复制代码
private void button2_Click(object sender, EventArgs e)
{
    TaskFactory factory = new TaskFactory();
    List<Task> taskList = new List<Task>();

    taskList.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
    taskList.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
    taskList.Add(factory.StartNew(() => { while (true) { } Console.WriteLine("查询接口"); }));

    // 异步非阻塞:任意任务完成后触发回调
    Task.WhenAny(taskList.ToArray()).ContinueWith(t =>
    {
        Console.WriteLine("至少一个异步操作执行完了");
    });

    // 主线程不阻塞,直接执行
    Console.WriteLine("主线程继续执行,不卡顿");
}

3. 注意事项

  • When 系列方法异步无阻塞,完美解决UI卡死问题,优先用于WinForm/WPF项目

  • 必须搭配 ContinueWith 使用,否则无法捕获任务完成事件

  • 主线程代码执行顺序优先于回调代码

  • 存在死循环任务时,WhenAll 回调永远不会触发


三、TaskFactory 自带回调:ContinueWhenAll / ContinueWhenAny

1. 核心原理

ContinueWhenAllContinueWhenAny 是任务工厂专属方法,属于语法糖,等价于 WhenAll/WhenAny + ContinueWith 的组合写法,代码更简洁。

  • ContinueWhenAll:所有任务完成后自动执行回调

  • ContinueWhenAny:任意一个任务完成后自动执行回调

2. 独立完整代码

cs 复制代码
private void button3_Click(object sender, EventArgs e)
{
    TaskFactory factory = new TaskFactory();
    List<Task> taskList = new List<Task>();

    taskList.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
    taskList.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
    taskList.Add(factory.StartNew(() => { while (true) { } Console.WriteLine("查询接口"); }));

    // 工厂自带回调:任意任务完成触发
    factory.ContinueWhenAny(taskList.ToArray(), (t) =>
    {
        Console.WriteLine("至少一个操作执行完了");
    });
}

3. 注意事项

  • 无需手动写 WhenAny/WhenAll 和 ContinueWith,封装度更高

  • 仅 TaskFactory 创建的任务集合推荐使用该写法

  • 本质仍是异步回调,不会阻塞主线程


四、Task 单个任务限时等待 Wait(int 毫秒)

1. 核心原理

单个 Task 自带 Wait(毫秒) 重载方法,作用是限时同步阻塞等待任务执行。等待时间内任务完成则直接放行,超时则主动结束等待,继续执行后续代码。

2. 独立完整代码

cs 复制代码
private void button4_Click(object sender, EventArgs e)
{
    Task t1 = new Task(() =>
    {
        Console.WriteLine("任务执行中");
        Thread.Sleep(2000); // 任务耗时2秒
    });

    t1.Start();
    // 限时等待1秒,超时自动放行
    t1.Wait(1000);
    Console.WriteLine("等待结束,执行后续代码");
}

3. 注意事项

  • 属于同步阻塞方法,限时期间会卡住UI线程

  • 可用于控制任务最大执行时长,防止任务无限阻塞程序

  • 超时后任务不会终止,仍在后台继续执行,只是主线程不再等待


五、核心知识点终极对比总结

  • 同步阻塞(卡死UI):WaitAll、WaitAny、Task.Wait(毫秒),适合控制台、后台同步逻辑

  • 异步非阻塞(推荐):WhenAll、WhenAny + ContinueWith,适合WinForm界面开发

  • 工厂简写:ContinueWhenAll/ContinueWhenAny,简化异步回调代码

  • 核心区别:Wait系列阻塞主线程,When系列不阻塞,仅通过回调处理任务结果


六、Task 同步/异步等待方法终极对比表

汇总所有等待方法的核心差异,清晰区分阻塞特性、用法、场景,为考试和开发高频考点。

|-----------|-----------------|----|------------------|----------------|---------------|--------------------|
| 方法分组 | 具体方法 | 阻塞 | 核心功能 | 配套写法 | 适用场景 | 核心特点 |
| 同步阻塞 | WaitAll | 是 | 等待全部任务完成放行 | 直接调用 | 控制台同步批量任务 | 顺序可控,卡死UI;死循环会永久阻塞 |
| 同步阻塞 | WaitAny | 是 | 等待任意一个任务完成放行 | 直接调用 | 多任务快速响应 | 响应快,阻塞UI,忽略剩余任务 |
| 同步阻塞 | Wait(毫秒) | 是 | 单个任务限时等待、超时放行 | 绑定单个Task | 限制任务最大时长 | 可超时管控,超时任务后台继续运行 |
| 异步非阻塞 | WhenAll | 否 | 全部任务完成触发回调 | 搭配ContinueWith | WinForm批量异步收尾 | UI流畅,需回调处理结果 |
| 异步非阻塞 | WhenAny | 否 | 任意任务完成触发回调 | 搭配ContinueWith | 多任务异步择优回调 | 高效不阻塞,需手动处理未完成任务 |
| 异步非阻塞 | ContinueWhen 系列 | 否 | 封装When+回调,一键执行 | TaskFactory专属 | 工厂任务简洁开发 | 代码极简,仅适配工厂创建任务 |

表格核心总结(必背考点)

  • 阻塞核心:Wait 系列全为同步阻塞,卡死UI;When 系列全为异步非阻塞,界面流畅

  • 功能等价:WaitAll ↔ WhenAll、WaitAny ↔ WhenAny,仅阻塞特性不同

  • 开发首选:窗体程序优先使用 When 系列,控制台可使用 Wait 系列

  • 语法糖:ContinueWhen 系列是 When+ContinueWith 的简化写法,专为任务工厂设计

相关推荐
左左右右左右摇晃9 小时前
手写Tomcat原理整理
java·开发语言·笔记·tomcat
孫治AllenSun9 小时前
【JVM】四大引用类型分析
java·开发语言·jvm
看-是灰机9 小时前
使用go语言实现对接
linux·开发语言·后端·docker·语言模型·golang·飞书
o5278831849 小时前
校园一体化管理系统2.1、基于整洁四层架构 + DDD+CQRS 项目目录解读
后端·架构·c#·asp.net·visual studio
人间凡尔赛18 小时前
Next.js 16 生产级实战:Cache Components + View Transitions 完整指南
开发语言·javascript·ecmascript
稚南城才子,乌衣巷风流1 天前
函数:编程中的核心概念
开发语言·前端·javascript
阿米亚波1 天前
【C++】流式数据输入处理(不完全整理)
开发语言·c++·笔记
大模型码小白1 天前
JAVA 集合框架进阶:List 与 Set 的深度解析与实战
java·开发语言·人工智能·windows·语言模型·list·ai编程