CancellationToken 的核心概念与设计哲学
CancellationToken 是 C# 异步编程模型中用于实现协作式取消的核心结构。它并非强制终止线程的机制,而是通过传递取消请求信号,让正在执行的任务能够以可控的方式响应中断。其设计哲学基于“协作”,即任务本身需要定期检查取消令牌的状态,并在收到取消请求时,主动清理资源并抛出 OperationCanceledException 异常来优雅地停止执行。这种机制有效避免了粗暴的线程终止所带来的资源泄漏和数据不一致风险,是构建高响应性、健壮性应用程序的基石。
CancellationTokenSource 与 CancellationToken 的关系
CancellationTokenSource 是取消信号的发起者,负责创建和管理 CancellationToken。一个 CancellationTokenSource 可以产生一个或多个关联的 CancellationToken。当调用 CancellationTokenSource 的 Cancel 方法时,所有由其产生的 Token 的状态都会变为“已请求取消”。开发者通常持有 CancellationToken 的实例,并在异步方法中传递它,而持有 CancellationTokenSource 的代码(如用户界面的事件处理器或另一个服务的生命周期管理器)则拥有触发取消的权力。这种职责分离的设计使得取消信号的产生和消费清晰解耦。
在异步方法中传播与检查 CancellationToken
最佳实践要求将 CancellationToken 作为异步方法的参数进行传播。对于支持取消的异步 API,其方法签名中通常包含一个可选的 CancellationToken 参数。在方法内部,可以通过几种主要方式来响应取消:
手动轮询 IsCancellationRequested 属性
在长时间运行的同步循环或计算中,开发者应定期检查 token.IsCancellationRequested 属性。如果检测到取消请求,则应抛出 OperationCanceledException 异常。这允许在循环的特定检查点安全中断。
使用 ThrowIfCancellationRequested 方法
这是最常用和推荐的检查方式。token.ThrowIfCancellationRequested() 是一个便捷方法,它会检查取消状态,如果取消已被请求,则立即抛出 OperationCanceledException。这行代码可以简洁地插入到异步方法中的多个逻辑点。
将 Token 传递给其他异步操作
当你的异步方法内部调用其他支持 CancellationToken 的异步方法(如 HttpClient.GetAsync、Task.Delay、文件 I/O 操作)时,应将接收到的 Token 继续向下传递。这使得取消请求能够沿着调用链传播,迅速中止整个操作链。
高级用法与最佳实践模式
要充分利用 CancellationToken,需要掌握一些超越基础用法的模式和技巧。
组合令牌:使用 CancellationTokenSource.CreateLinkedTokenSource
在实际应用中,一个操作可能同时受多个取消条件约束(例如,用户请求取消和操作超时)。此时,可以使用 CancellationTokenSource.CreateLinkedTokenSource 方法,将多个现有的 CancellationToken 合并成一个“链接令牌”。当任何一个源令牌被取消时,这个链接令牌也会被取消。这为处理复杂的取消逻辑提供了极大的灵活性。
超时与取消的集成
利用 CancellationTokenSource 的构造函数 CancellationTokenSource(TimeSpan delay) 可以轻松实现超时功能。创建一个在指定时间后自动取消的 TokenSource,并将其 Token 传递给异步方法,可以有效防止操作无限制地等待。
资源清理与 finally 块
即使操作被取消,也必须确保分配的资源得到释放。在 try-catch-finally 结构中,取消异常通常在 catch 块中处理或重新抛出,而资源清理代码应放在 finally 块中。从 .NET 6 开始,CancellationToken 还可以注册回调(Register 方法),在取消发生时执行清理动作,这为实现更及时的清理提供了另一种途径。
与任务(Task)的集成和异常处理
理解 CancellationToken 如何影响 Task 的状态至关重要。一个因 CancellationToken 而取消的任务,其 Status 会变为 TaskStatus.Canceled,而非 Faulted。在等待此类任务时,会抛出 OperationCanceledException(或派生的 TaskCanceledException)。因此,在调用方的异常处理中,应专门捕获这些异常以区分操作是被取消还是因错误而失败。通常,取消不被视为一个需要向上传播的“错误”,而是一个正常的控制流事件。
避免常见陷阱
一个常见的错误是捕获了 OperationCanceledException 后却又将其吞没(不重新抛出),这可能导致调用方无法知晓任务已被取消。另一个陷阱是在异步方法中忽略了传递来的 CancellationToken,使得该方法无法被外部取消。此外,应注意避免在令牌已取消后启动不必要的昂贵操作,应在方法开始时立即检查令牌状态。
总结
CancellationToken 是构建响应迅速、资源管理得当的现代 C# 应用程序不可或缺的工具。通过理解其协作式取消的哲学,掌握其基础用法和 CreateLinkedTokenSource 等高级模式,并遵循在异步方法中积极传播和检查令牌的最佳实践,开发者可以编写出能够优雅处理用户中断、超时和整体应用程序关机等场景的健壮代码。正确使用 CancellationToken 最终将带来更好的用户体验和更稳定的系统行为。
900

被折叠的 条评论
为什么被折叠?



