1. 理解CancellationTokenSource的核心价值
在.NET开发中,异步编程已经成为标配,而任务取消机制则是异步编程中不可或缺的重要部分。CancellationTokenSource(CTS)就是.NET为我们提供的任务取消解决方案。我第一次真正意识到它的重要性是在开发一个长时间运行的数据处理服务时——当用户突然要求停止处理,如果没有合适的取消机制,要么只能暴力终止进程,要么眼睁睁看着它继续消耗资源。
CTS本质上是一个信号发射器,它创建的CancellationToken则是信号接收器。这种生产者-消费者模式的分离设计非常巧妙,让取消逻辑和执行逻辑能够解耦。想象一下餐厅的点餐系统:厨师(任务执行方)不需要知道是谁取消了订单(厨房门口的取消按钮),只要收到取消通知就会停止当前餐品的制作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. CancellationTokenSource的工作原理
2.1 核心组件交互模型
CancellationTokenSource的内部实现其实非常精妙。当我们创建一个新的CTS实例时,实际上初始化了两个关键组件:
- 一个ManualResetEvent用于线程同步
- 一个注册回调的链表(RegistrationNode)
当调用Cancel()方法时,会发生以下原子操作:
- 设置取消状态标志位
- 触发ManualResetEvent
- 遍历执行所有注册的回调函数
csharp复制// 典型创建和使用模式
var cts = new CancellationTokenSource();
var token = cts.Token;
// 注册取消回调
token.Register(() => {
Console.WriteLine("Cancellation requested!");
});
// 在另一个任务中检查取消状态
Task.Run(() => {
while(!token.IsCancellationRequested) {
// 模拟工作
Thread.Sleep(100);
}
});
// 请求取消
cts.Cancel();
2.2 超时取消的高级用法
除了手动取消,CTS还支持超时自动取消,这在处理网络请求时特别有用:
csharp复制// 5秒后自动取消
var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
// 链接多个CTS(任意一个触发即取消)
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
cts1.Token,
cts2.Token
);
3. 实战中的最佳实践
3.1 资源清理的正确姿势
很多开发者容易忽略取消时的资源清理。CTS与IDisposable接口的实现意味着我们需要妥善处理它的生命周期:
csharp复制// 使用using语句确保释放
using(var cts = new CancellationTokenSource()) {
// ...业务代码
} // 自动调用Dispose()
在Dispose方法中,CTS会:
- 取消所有待处理的回调
- 释放ManualResetEvent
- 清理注册链表
3.2 异常处理策略
当任务被取消时,常见的模式是抛出OperationCanceledException。但处理时有几个细节需要注意:
csharp复制try {
await LongRunningOperationAsync(token);
} catch (OperationCanceledException ex) when (token.IsCancellationRequested) {
// 明确区分是我们主动取消的情况
Console.WriteLine("Operation was gracefully cancelled");
} catch (OperationCanceledException) {
// 其他类型的取消
}
4. 性能优化技巧
4.1 回调注册的代价
每个Token.Register()调用都会在堆上分配一个新的RegistrationNode对象。对于高频调用的热路径,这可能导致GC压力:
csharp复制// 不推荐在循环中频繁注册
while(condition) {
token.Register(() => {...}); // 每次循环都分配新对象
}
// 推荐做法:单次注
