c# CancellationToken.Register 的用法和注意事项
技术百科
煙雲
发布时间:2026-01-27
浏览: 次 CancellationToken.Register 在 CancellationTokenSource.Cancel() 被调用或 CancelAfter() 到期后触发,仅一次且仅当 token 未被释放;回调默认同步执行于取消线程,异常会被吞掉,需 try/catch;必须显式 Unregister 或 using 管理生命周期。
CancellationToken.Register 什么时候会触发?
它只在 CancellationTokenSource.Cancel() 被调用(或 CancelAfter() 到期)后触发,且仅当该 CancellationToken 尚未被释放(比如 cts 已 Dispose())时才安全执行。不是“注册就立刻跑”,也不是“每次轮询都调”,而是纯事件式回调——一次取消,最多触发一次(除非重复注册多个委托)。
- 回调在取消信号发出后、异步任务真正退出前执行,常用于资源清理、日志记录、状态重置
- 如果
token来自已Dispose()的CancellationTokenSource,Register不报错但回调永远不会执行 - 回调默认在取消发生的线程上同步执行(比如你在 UI 线程调
cts.Cancel(),回调就在 UI 线程跑),可能阻塞主线程 —— 若需异步或切线程,得自己包装Task.Run或用async+await配合TaskScheduler
CancellationToken.Register 的参数陷阱
最常用的是 Register(Action) 重载,但它有隐藏行为:如果回调里抛异常,整个取消流程不会中断,但异常会被吞掉(.NET 默认不传播),你根本看不到错误 —— 这是线上排查“为什么清理没做”的高频盲区。
- 务必在回调内部加
try/catch,尤其涉及文件关闭、数据库连接释放等操作 - 带
state参数的重载(Register(Action)更安全:可传入IDisposable实例,避免闭包捕获导致对象生命周期延长 - 不要传入异步 lambda(如
() => await DoCleanupAsync())——Register只接受同步委托,await会被忽略,变成火种式执行(fire-and-forget),极易丢失异常和上下文
注册后怎么取消注册?
返回值 CancellationTokenRegistration 是结构体,必须显式调用 Unregister() 才能解除绑定;否则即使 token 已失效,只要 cts 没被 GC,回调仍可能被调用(尤其在反复创建/取消的循环场景中)。
- 推荐用
using声明(因为CancellationTokenRegistration实现了IDisposable):using var registration = token.Register(() => Console.WriteLine("cleanup")); - 若注册后提前想撤回(比如条件变更不再需要清理),直接调
registration.Unregister(),它返回bool表示是否成功(已触发则返回false) - 别依赖 GC 回收自动解绑 ——
CancellationTokenRegistration不含终结器,不回收也不会泄露内存,但逻辑上容易“多清一次”或“漏清”
和 ThrowIfCancellationRequested() 混用要注意什么?
Register 和 ThrowIfCancellationRequested() 完全不同层:前者是“取消后干点啥”,后者是“我正干活,检查下要不要停”。它们可以共存,但顺序和时机很关键。
- 如果你在
Task.Run内部一边循环一边调token.ThrowIfCancellationRequested(),又在外层token.Register注册了清理回调 —— 那么一旦触发取消,回调会在OperationCanceledException抛出**之后、任务彻底结束之前**执行 - 但若你在回调里又去调
token.ThrowIfCancellationRequested(),会直接抛二次异常(因为 token 已是取消态),导致未处理异常崩溃 - 典型误用:
token.Register(() => { if (token.IsCancellationRequested) DoCleanup(); })—— 多余,Register触发本身就意味着已取消,不用再查
Register,而是想清楚:这个清理动作,是不是必须在取消那一刻发生?有没有竞态?会不会被重复触发?要不要跨线程?这些细节不抠,上
# ai
# 的是
# 这是
# 你在
# 多个
# 最多
# 会不会
# 什么时候
# 就在
# ui
# 循环
# 对象
# if
# c#
# 委托
# 数据库
# .net
# 为什么
# 线程
# 要不要
# 异步
# 事件
# register
# 回调
# 结构体
# Token
# try
# catch
# 闭包
# Object
# Lambda
# bool
# 主线程
# 异步任务
# using
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- php转mp4怎么设置帧率_调整php生成mp4视
- c++怎么使用类型萃取type_traits_c+
- Win11怎么关闭小组件_Win11禁用任务栏天气
- Win11色盲模式怎么开_Win11屏幕颜色滤镜设
- Django 测试数据库表缺失与字段未创建问题的完
- Win11怎么设置夜间模式_Windows11显示
- Win10系统怎么查看端口状态_Windows10
- Win11怎么关闭VBS安全性_Windows11
- 如何在Golang中优化文件读写性能_使用缓冲和并
- php485返回数据不完整怎么办_php485数据
- Mac怎么开启“任何来源”_Mac安装未签名应用的
- 如何使用正则表达式精确匹配最多含一个换行符的 st
- Ajax提交表单PHP怎么接收_处理Ajax发送的
- Win11怎么查看硬盘型号_Windows 11检
- Windows系统被恶意软件破坏后的恢复策略_错误
- 如何将竖排文本文件转换为横排字符串
- Win11怎么设置声音输出设备_Windows11
- c++的mutex和lock_guard如何使用
- 如何在Golang中处理URL参数_Golang
- Windows 11如何开启文件夹加密(EFS)_
- Win10如何卸载Skype_Win10卸载Sky
- Win11怎么打开旧版计算器_Win11恢复传统计
- Win11怎么制作U盘启动盘_Win11原版系统安
- Linux怎么实现内网穿透_Linux安装Frp客
- c++怎么调用nana库开发GUI_c++ 现代风
- Win11声音忽大忽小怎么办 Win11音频增强功
- Python生成器表达式内存优化_惰性计算说明【指
- c++中如何使用std::variant_c++1
- c++ std::future和std::prom
- c++ std::atomic如何保证原子性 c+
- windows 10应用商店区域怎么改_windo
- Win10如何更改开机密码_Windows10登录
- php订单日志怎么记录发货_php记录订单发货操作
- Python性能剖析高级教程_cProfileLi
- MAC如何安装Git版本控制工具_MAC开发环境配
- 如何使用Golang recover捕获panic
- 如何在Golang中处理模块包路径变化_Golan
- 如何在 Go 中正确测试带 Cookie 的 HT
- Python模块的__name__属性如何由导入方
- php打包exe怎么传递参数_命令行参数接收方法【
- Windows怎样关闭开始菜单广告_Windows
- Win11怎么开启HDR模式_Windows 11
- Mac上的iMovie如何剪辑视频?(新手入门教程
- Windows笔记本无法进入睡眠模式怎么办?(电源
- 如何在Golang中验证模块完整性_Golangg
- ACF 教程:正确更新嵌套在多层 Group 字段
- php下载安装包太大怎么下载_分卷压缩下载方法【教
- Win10怎样清理C盘Steam游戏缓存_Win1
- Windows 10怎么隐藏特定更新补丁_Wind
- 如何在Golang中实现服务熔断与限流_Golan

QQ客服