笔记承接 :承接Task获取异步结果笔记,本节课核心四大方法:WaitAll、WaitAny、WhenAll、WhenAny
核心区分:带Wait开头=同步阻塞;带When开头=异步非阻塞;搭配任务工厂回调,补齐限时等待用法
窗体控件:Button1~Button4,分别对应4组对比案例
二、前置基础概念
2.1 TaskFactory 任务工厂
-
作用:批量创建、调度、管理大批量异步任务
-
factory.StartNew():工厂创建并自动启动任务,等价 Task.Run() -
优势:批量管理任务、自带完成回调方法,适合多任务并发场景
2.2 死循环代码作用
案例中第三个任务编写 while(true) 死循环:永久不会执行完毕,用来测试多任务等待效果
2.3 方法总分类(考试必考)
-
✅ 同步阻塞方法:Task.WaitAll()、Task.WaitAny() → 卡死UI
-
✅ 异步非阻塞方法:Task.WhenAll()、Task.WhenAny() → 不卡死UI
三、案例1:同步阻塞 WaitAll & WaitAny(Button1)
3.1 核心原理
-
Task.WaitAll() :同步等待,必须等待集合内所有任务全部执行完毕,才执行后续代码
-
Task.WaitAny() :同步等待,只要集合内任意一个任务执行完毕,立刻放行执行后续代码
-
共性:两个都是同步方法,执行时阻塞主线程,直接卡死窗体
3.2 逐行拆分带注释代码
private void button1_Click(object sender, EventArgs e)
{
//1.实例化任务工厂
TaskFactory factory = new TaskFactory();
//2.创建任务集合,批量存储异步任务
List<Task> list = new List<Task>();
//3.批量添加并发任务
list.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
list.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
//第三个任务:死循环,永久无法执行完毕
list.Add(factory.StartNew(() =>
{
while (true) { }
Console.WriteLine("查询接口");
}));
//========两组代码二选一测试========
//Task.WaitAll(list.ToArray());
//同步阻塞:需要等待3个任务全部完成,第三个死循环永久卡死,代码永不向下执行
Task.WaitAny(list.ToArray());
//同步阻塞:只要任意一个任务执行完毕,直接放行
//数据库/缓存任务率先执行完毕,直接打印末尾日志,不会卡死
Console.WriteLine("所有的操作执行完了");
}
3.3 运行现象
-
开启WaitAll:程序直接卡死,控制台不打印末尾文字
-
开启WaitAny:瞬间放行,打印日志,但是依旧阻塞UI
四、案例2:异步非阻塞 WhenAll & WhenAny(Button2)
4.1 核心原理
-
WhenAll、WhenAny:纯异步方法,不会阻塞主线程,窗体永不卡死
-
返回值:返回Task任务,必须搭配 ContinueWith 接收回调结果
-
和Wait方法功能一模一样,唯一区别:是否阻塞UI
4.2 代码拆分注释
private void button2_Click(object sender, EventArgs e)
{
TaskFactory factory = new TaskFactory();
List<Task> list = new List<Task>();
list.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
list.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
list.Add(factory.StartNew(() =>
{
while (true) { }
Console.WriteLine("查询接口");
}));
//========两组代码二选一测试========
//WhenAll:全部任务执行完毕后,触发回调,异步不阻塞
//Task.WhenAll(list.ToArray()).ContinueWith(t =>
//{
// Console.WriteLine("所有的操作执行完了");
//});
//WhenAny:任意一个任务执行完毕,触发回调
Task.WhenAny(list.ToArray()).ContinueWith(t =>
{
Console.WriteLine("至少一个异步操作执行完了");
});
}
4.3 同步VS异步对比(必考)
-
WaitAll / WaitAny:同步阻塞,卡住UI,简单粗暴,项目禁用
-
WhenAll / WhenAny:异步回调,不卡UI,搭配ContinueWith,开发首选
五、案例3:任务工厂自带回调 ContinueWhen(Button3)
5.1 方法释义
TaskFactory内置专属回调方法,简化When+ContinueWith写法,等价封装:
-
ContinueWhenAll = WhenAll + ContinueWith 封装版
-
ContinueWhenAny = WhenAny + ContinueWith 封装版
5.2 拆分代码
private void button3_Click(object sender, EventArgs e)
{
TaskFactory factory = new TaskFactory();
List<Task> list = new List<Task>();
list.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
list.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
list.Add(factory.StartNew(() =>
{
while (true) { }
Console.WriteLine("查询接口");
}));
//全部任务完成回调
//factory.ContinueWhenAll(list.ToArray(), (t) =>
//{
// Console.WriteLine("所有的操作都执行完了");
//});
//任意一个任务完成回调(本案例执行)
factory.ContinueWhenAny(list.ToArray(), (t) =>
{
Console.WriteLine("至少一个操作执行完了");
});
}
5.3 使用场景
批量创建任务、统一调度场景,优先使用任务工厂自带回调,代码更加简洁
六、案例4:任务限时等待 Wait(毫秒)(Button4)
6.1 方法作用
任务对象.Wait(int millisecondsTimeout):限时阻塞等待
-
作用:等待当前单个任务执行,设置最大等待时长
-
任务提前执行完毕:立刻放行
-
任务超时未执行完毕:放弃等待,直接放行
6.2 逐行解析
private void button4_Click(object sender, EventArgs e)
{
Task t1 = new Task(() =>
{
Console.WriteLine("的函数点击发送");
});
t1.Start();
//限时等待1000毫秒:最多阻塞1秒
t1.Wait(1000);
Console.WriteLine("sassss");
}
6.3 拓展辨析
-
t1.Wait():无参,永久等待任务完成,无限阻塞 -
t1.Wait(1000):有参,限时等待,超时自动放行
七、四大方法超级对照表(背诵版)
| 方法名称 | 同步/异步 | 执行规则 | 是否阻塞UI |
|---|---|---|---|
| Task.WaitAll | 同步 | 等待全部任务完成 | ✅ 阻塞 |
| Task.WaitAny | 同步 | 等待任意一个任务完成 | ✅ 阻塞 |
| Task.WhenAll | 异步 | 等待全部任务完成 | ❌ 不阻塞 |
| Task.WhenAny | 异步 | 等待任意一个任务完成 | ❌ 不阻塞 |
八、易错考点汇总
-
带Wait 全部是同步、阻塞;带When全部是异步、非阻塞(秒杀选择题)
-
When系列方法必须配合ContinueWith接收结果,不能直接写后续业务
-
ContinueWhenAll、ContinueWhenAny = When封装版,任务工厂专属
-
Wait()无参永久阻塞,传入毫秒实现限时等待
-
存在死循环任务:WaitAll永久卡死,WaitAny正常放行
九、完整版可运行代码
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace _6waitAny和waitall_whena
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TaskFactory factory = new TaskFactory();
List<Task> list = new List<Task>();
list.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
list.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
list.Add(factory.StartNew(() =>
{
while (true) { }
Console.WriteLine("查询接口");
}));
//Task.WaitAll(list.ToArray());
Task.WaitAny(list.ToArray());
Console.WriteLine("所有的操作执行完了");
}
private void button2_Click(object sender, EventArgs e)
{
TaskFactory factory = new TaskFactory();
List<Task> list = new List<Task>();
list.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
list.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
list.Add(factory.StartNew(() =>
{
while (true) { }
Console.WriteLine("查询接口");
}));
//Task.WhenAll(list.ToArray()).ContinueWith(t =>
//{
// Console.WriteLine("所有的操作执行完了");
//});
Task.WhenAny(list.ToArray()).ContinueWith(t =>
{
Console.WriteLine("至少一个异步操作执行完了");
});
}
private void button3_Click(object sender, EventArgs e)
{
TaskFactory factory = new TaskFactory();
List<Task> list = new List<Task>();
list.Add(factory.StartNew(() => { Console.WriteLine("查询数据库"); }));
list.Add(factory.StartNew(() => { Console.WriteLine("查询缓存"); }));
list.Add(factory.StartNew(() =>
{
while (true) { }
Console.WriteLine("查询接口");
}));
//factory.ContinueWhenAll(list.ToArray(), (t) =>
//{
// Console.WriteLine("所有的操作都执行完了");
//});
factory.ContinueWhenAny(list.ToArray(), (t) =>
{
Console.WriteLine("至少一个操作执行完了");
});
}
private void button4_Click(object sender, EventArgs e)
{
Task t1 = new Task(() =>
{
Console.WriteLine("的函数点击发送");
});
t1.Start();
t1.Wait(1000);
Console.WriteLine("sassss");
}
}
}