CancellationTokenSource cts
是用于控制任务(线程)停止运行的。我们一步步来解释它的作用。
🔍 现在的代码结构大概是这样的:
Task.Run(() => {while (true){// 不断循环采集图像}
});
这种写法虽然简单,但最大的问题是:
- 这个线程永远停不下来。
- 你无法在外部优雅地告诉它“嘿,停一下”,除非强行杀掉进程。
✅ 而 CancellationTokenSource
就是为了解决这个问题。
📌 它的作用:
提供一种机制,可以从外部请求取消正在运行的任务或线程。
🧠 举个简单的例子
CancellationTokenSource cts = new CancellationTokenSource();Task.Run(() =>
{while (!cts.Token.IsCancellationRequested){Console.WriteLine("工作中...");Thread.Sleep(1000);}Console.WriteLine("收到取消信号,退出循环!");
});// 5秒后取消
Thread.Sleep(5000);
cts.Cancel();
输出:
工作中...
工作中...
工作中...
工作中...
工作中...
收到取消信号,退出循环!
🚦 实际在你的程序中,使用它的意义是:
- 将来你关闭程序或点击“停止”按钮时,
- 你只需调用
cts.Cancel()
, - 运行中的
while (!cts.Token.IsCancellationRequested)
就会安全退出。
🔁 没有它会发生什么?
如果你一直用 while (true)
,即使界面关闭、用户点击“停止”,后台线程仍然在跑,非常浪费资源,甚至会造成程序无法正常退出或 CPU 飙高。
✅ 总结一句话:
CancellationTokenSource
就像一个“遥控开关”,可以让后台线程知道“该停下来了”。
它和while中放个bool变量有啥区别
“在 while
中放一个 bool
变量”确实也是一种控制循环退出的方式,那它和 CancellationTokenSource
有什么区别呢?我们来做个对比👇
🆚 bool
控制变量 vs CancellationToken
对比项 | bool 控制变量 | CancellationTokenSource |
---|---|---|
✅ 基本功能 | 控制循环停止 | 控制循环停止 |
👨💻 用法简单 | 非常简单(比如 while (running) ) | 稍复杂,需要 cts.Token 等对象 |
✅ 多任务控制 | 需要共享变量或加锁 | 天生就支持多个任务共享同一个 token |
❌ 线程安全性 | 需要你手动保证线程安全(如加 volatile 或 lock ) | 自带线程安全,不容易出错 |
🔄 与 Task / async / Parallel 等配合 | 兼容差,不好配合现代并发工具 | 与 Task.Run 、Parallel.ForEach 、async/await 等无缝集成 |
💣 取消等待 | 无法中断 Task.Delay / Wait 等 | 可中断等待任务,比如 Task.Delay(..., token) |
🎯 最佳应用场景 | 简单循环退出 | 复杂并发任务、任务取消、超时控制等 |
🔍 示例对比:
✅ 用 bool
控制变量:
volatile bool running = true;Task.Run(() =>
{while (running){// 采图}
});// 关闭时
running = false;
注意这里用了 volatile
是因为如果不写,可能在多线程中有缓存失效的问题(CPU 缓存没及时刷新变量值)。
✅ 用 CancellationToken
:
CancellationTokenSource cts = new CancellationTokenSource();Task.Run(() =>
{while (!cts.Token.IsCancellationRequested){// 采图}
});// 关闭时
cts.Cancel();
这样可以安全、优雅地中止线程,还可以用于 Task.Delay
、Parallel.ForEach
、HttpClient.SendAsync
等现代 API。
深入探讨
逐条举例 + 拆解对比,让它变得通俗易懂且能立刻理解!
✅ 多任务控制(多线程共享控制)
🔸 bool
的写法:
你得写成这样👇
volatile bool _running = true;Task.Run(() =>
{while (_running){// 做事...}
});Task.Run(() =>
{while (_running){// 另一个线程做事...}
});
- 如果多个任务要响应这个
bool
,你得自己保证它是线程安全的(加volatile
), - 若再多几个线程,管理起来越来越麻烦。
✅ CancellationToken
的写法(天然支持):
var cts = new CancellationTokenSource();
var token = cts.Token;Task.Run(() => Work(token));
Task.Run(() => Work(token));void Work(CancellationToken token)
{while (!token.IsCancellationRequested){// 干活...}
}
✅ 所有任务共用一个 token,控制统一、安全、优雅,代码看起来也很干净。
❌ 线程安全性
🔸 bool
写法:
必须加 volatile
或 lock
,否则有可能一个线程改了,另一个线程根本“看不见”:
volatile bool _running = true;// 不加 volatile,可能一个线程死循环都不会停
或者你得用 lock
,比如:
lock (_lockObj)
{_running = false;
}
复杂且容易忘。
✅ CancellationToken
- 内部已经做好线程安全,不用你操心
.IsCancellationRequested
是线程安全的读操作.Cancel()
会安全地广播给所有监听者
🔄 与 Task / async / Parallel 等配合
🔸 bool
写法:
比如你想写个 Task.Delay(5000)
,中途取消:
await Task.Delay(5000); // 没法中断!
你只能写复杂逻辑轮询 bool
,还不能打断系统 API。
✅ CancellationToken
:
await Task.Delay(5000, token); // 支持取消!
同样适用于:
Parallel.ForEach(..., token, ...)
HttpClient.SendAsync(..., token)
Task.Run(() => ..., token)
它和这些 现代异步/并发工具是“原生支持”的搭档。
🧠 总结一句话:
bool
适合非常简单的控制,一两个线程内部用可以;但如果你要和 Task、多个线程、现代异步配合,CancellationToken
才是真正线程安全、优雅且可组合的现代解决方案。
✅ 建议
- 简单项目、没用多线程/异步的地方 →
bool
就够了。 - 多线程、用
Task
、async/await
、任务并发的项目 → 建议用CancellationToken
,更稳更现代。
如果你目前只是用一个线程干活,可以继续用 bool
,但随着项目复杂度提升,CancellationToken
会更适合并发环境,特别是你这个多相机+高速采集的系统。以后维护、扩展也更舒服。
2025年3月29日---------------新增内容:
Task.Delay
的第二个参数不是 CancellationTokenSource
,而是它里面的 CancellationToken
。
✅ 正确语法:
var cts = new CancellationTokenSource();
await Task.Delay(5000, cts.Token); // 注意这里是 cts.Token,不是 cts 本身
❓为什么不是 cts
本身?
CancellationTokenSource
是控制器:可以.Cancel()
来通知取消。CancellationToken
是“令牌”:传给任务或方法来监听是否被取消。
// 你控制取消:
cts.Cancel();// 你监听是否被取消:
token.IsCancellationRequested
就像你开会:
CancellationTokenSource
是主持人(可以宣布“散会!”)CancellationToken
是给每个人的耳机(听到“散会”指令)
🚫 错误写法(会编译错误):
await Task.Delay(5000, cts); // ❌ 错!类型不匹配
✅ 正确写法:
await Task.Delay(5000, cts.Token); // ✅