当前位置: 首页 > news >正文

C# 取消机制(CancellationTokenSource/CancellationToken)

C# 取消机制

什么是取消机制?

想象一下这个场景:你在网上下载一个大文件,突然发现下错了,点击"取消"按钮。你期望的是下载立即停止,而不是程序卡死或者继续在后台下载完。

C# 的取消机制就是解决这个问题的!它是一种协作式的取消方式,意思是:

  • 不是强制终止:不像强行关闭程序那样粗暴

  • 礼貌地请求停止:向正在执行的任务发送"请停止"的信号

  • 任务自己决定如何停止:任务收到信号后,可以安全地保存状态、释放资源,然后优雅退出


创建和基本使用

using System;
using System.Threading;
using System.Threading.Tasks;class Program
{static void Main(){// 创建 CancellationTokenSourcevar cts = new CancellationTokenSource();// 启动一个可取消的任务Task.Run(() => DoWork(cts.Token));// 等待用户输入后取消Console.ReadLine();cts.Cancel();  // 取消Console.WriteLine("取消信号已发送");Console.ReadLine();}static void DoWork(CancellationToken cancellationToken){try{for (int i = 0; i < 100; i++){// 检查取消请求cancellationToken.ThrowIfCancellationRequested();Console.WriteLine($"工作进度: {i}%");Thread.Sleep(100);}}catch (OperationCanceledException){Console.WriteLine("操作已被取消");}}
}

完整的生活化例子

让我们用一个更贴近生活的例子来解释:

场景:厨房做饭

using System;
using System.Threading;
using System.Threading.Tasks;class Kitchen
{static async Task Main(){// 厨师开始做饭var chef = new Chef();// 客人有一个"取消按钮"(如果等不及可以取消订单)var guestCancellation = new CancellationTokenSource();// 厨房也有一个超时限制(30分钟没做完自动取消)var kitchenTimeout = new CancellationTokenSource(TimeSpan.FromMinutes(30));// 组合两个取消源:客人取消 OR 厨房超时using var combinedCancellation = CancellationTokenSource.CreateLinkedTokenSource(guestCancellation.Token, kitchenTimeout.Token);try{// 开始做饭!Console.WriteLine("厨师开始准备餐点...");await chef.CookMealAsync(combinedCancellation.Token);Console.WriteLine("餐点准备完成!");}catch (OperationCanceledException) when (guestCancellation.Token.IsCancellationRequested){Console.WriteLine("客人取消了订单");}catch (OperationCanceledException) when (kitchenTimeout.Token.IsCancellationRequested){Console.WriteLine("厨房超时,无法完成订单");}// 模拟客人取消Console.WriteLine("\n按回车键模拟客人取消订单...");Console.ReadLine();guestCancellation.Cancel(); // 取消}
}class Chef
{public async Task CookMealAsync(CancellationToken cancellationToken){string[] steps = { "准备食材", "切菜", "烹饪", "摆盘", "上菜" };foreach (var step in steps){// 检查是否收到取消信号cancellationToken.ThrowIfCancellationRequested();Console.WriteLine($"正在: {step}");await Task.Delay(2000, cancellationToken); // 模拟每个步骤需要2秒}}
}


核心类详解

1. CancellationTokenSource 类

这是取消操作的发起者,负责创建和管理取消信号。

构造函数
// 1. 默认构造函数 - 创建未取消的源
var cts1 = new CancellationTokenSource();// 2. 带超时的构造函数 - 指定时间后自动取消
var cts2 = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // 5秒后取消
var cts3 = new CancellationTokenSource(5000); // 5000毫秒后取消
主要属性
public class CancellationTokenSourceProperties
{public static void DemonstrateProperties(){var cts = new CancellationTokenSource();// Token: 获取与此源关联的 CancellationTokenCancellationToken token = cts.Token;Console.WriteLine($"Token: {token}");// IsCancellationRequested: 检查是否已请求取消bool isCancelled = cts.IsCancellationRequested;Console.WriteLine($"是否已取消: {isCancelled}"); // 初始为 false}
}
主要方法
public class CancellationTokenSourceMethods
{public static void DemonstrateMethods(){var cts = new CancellationTokenSource();// Cancel(): 请求取消操作Console.WriteLine("调用 Cancel() 方法...");cts.Cancel();Console.WriteLine($"取消状态: {cts.IsCancellationRequested}"); // true// CancelAfter(): 在指定时间后自动取消var delayedCts = new CancellationTokenSource();delayedCts.CancelAfter(TimeSpan.FromSeconds(3)); // 3秒后自动取消Console.WriteLine($"3秒后将自动取消: {delayedCts.IsCancellationRequested}"); // false// Dispose(): 释放资源cts.Dispose();delayedCts.Dispose();}
}
静态方法
public class CancellationTokenSourceStaticMethods
{public static void DemonstrateStaticMethods(){var cts1 = new CancellationTokenSource();var cts2 = new CancellationTokenSource();// CreateLinkedTokenSource(): 创建链接的取消源// 当任意一个源取消时,链接的源也会取消using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);Console.WriteLine("当 cts1 或 cts2 取消时,linkedCts 也会自动取消");// 测试链接效果cts1.Cancel();Console.WriteLine($"cts1 取消状态: {cts1.IsCancellationRequested}"); // trueConsole.WriteLine($"linkedCts 取消状态: {linkedCts.IsCancellationRequested}"); // true}
}

2. CancellationToken 结构

这是取消操作的接收者,用于检测取消请求。

主要属性
public class CancellationTokenProperties
{public static void DemonstrateProperties(CancellationToken token){// IsCancellationRequested: 是否已请求取消Console.WriteLine($"是否请求取消: {token.IsCancellationRequested}");// CanBeCanceled: 此令牌能否被取消Console.WriteLine($"能否被取消: {token.CanBeCanceled}");// WaitHandle: 获取等待取消信号的可等待句柄Console.WriteLine($"WaitHandle: {token.WaitHandle}");// None: 静态属性,返回空的无法取消的令牌var emptyToken = CancellationToken.None;Console.WriteLine($"空令牌能否取消: {emptyToken.CanBeCanceled}"); // false}
}
主要方法
public class CancellationTokenMethods
{public static void DemonstrateMethods(){var cts = new CancellationTokenSource();var token = cts.Token;// ThrowIfCancellationRequested(): 如果已请求取消,则抛出 OperationCanceledExceptiontry{token.ThrowIfCancellationRequested(); // 此时不会抛出异常Console.WriteLine("没有取消请求,继续执行...");cts.Cancel(); // 请求取消token.ThrowIfCancellationRequested(); // 这里会抛出异常}catch (OperationCanceledException){Console.WriteLine("捕获到 OperationCanceledException");}// Register(): 注册取消时的回调函数var callbackCts = new CancellationTokenSource();var callbackToken = callbackCts.Token;// 注册回调 - 当取消时自动执行CancellationTokenRegistration registration = callbackToken.Register(() => {Console.WriteLine("取消回调被执行!");Console.WriteLine("执行清理操作...");});Console.WriteLine("注册了取消回调");callbackCts.Cancel(); // 触发回调// 可以取消注册registration.Dispose();Console.WriteLine("回调已取消注册");}
}


实际应用场景

场景1:文件下载器

public class FileDownloader
{public async Task DownloadFileAsync(string url, string savePath, IProgress<int> progress = null,CancellationToken cancellationToken = default){using var httpClient = new HttpClient();try{// 开始下载using var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);response.EnsureSuccessStatusCode();// 获取文件总大小var totalBytes = response.Content.Headers.ContentLength ?? -1;using var stream = await response.Content.ReadAsStreamAsync();using var fileStream = new FileStream(savePath, FileMode.Create);var buffer = new byte[8192];int bytesRead;long totalRead = 0;// 读取数据并写入文件while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0){cancellationToken.ThrowIfCancellationRequested();await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);totalRead += bytesRead;// 报告进度if (totalBytes > 0 && progress != null){int percentage = (int)((totalRead * 100) / totalBytes);progress.Report(percentage);}}Console.WriteLine("下载完成!");}catch (OperationCanceledException){// 如果文件已部分下载,删除不完整的文件if (File.Exists(savePath))File.Delete(savePath);Console.WriteLine("下载已被取消");throw;}}
}// 使用示例
class Program
{static async Task Main(){var downloader = new FileDownloader();var cts = new CancellationTokenSource();// 创建进度报告器var progress = new Progress<int>(percent =>{Console.WriteLine($"下载进度: {percent}%");});// 启动下载任务var downloadTask = downloader.DownloadFileAsync("https://example.com/largefile.zip","largefile.zip",progress,cts.Token);// 模拟用户取消(5秒后)_ = Task.Delay(5000).ContinueWith(_ => {Console.WriteLine("用户取消了下载");cts.Cancel();});try{await downloadTask;}catch (OperationCanceledException){Console.WriteLine("下载任务已成功取消");}}
}

场景2:数据库查询超时

public class DatabaseService
{public async Task<List<Product>> SearchProductsAsync(string keyword,TimeSpan timeout,CancellationToken externalToken = default){// 创建查询超时using var timeoutCts = new CancellationTokenSource(timeout);// 组合令牌:外部取消 OR 超时取消using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeoutCts.Token);try{// 模拟数据库查询await Task.Delay(2000, linkedCts.Token); // 假设查询需要2秒// 这里应该是真实的数据库查询代码// using var connection = new SqlConnection(connectionString);// await connection.OpenAsync(linkedCts.Token);// 执行查询...return new List<Product>{new Product { Id = 1, Name = $"{keyword} 产品1" },new Product { Id = 2, Name = $"{keyword} 产品2" }};}catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested){throw new TimeoutException($"数据库查询超时,超过 {timeout.TotalSeconds} 秒");}}
}public class Product
{public int Id { get; set; }public string Name { get; set; }
}


属性方法总结表

CancellationTokenSource 主要成员

成员类型说明
Token属性获取关联的 CancellationToken
IsCancellationRequested属性检查是否已请求取消
Cancel()方法请求取消操作
CancelAfter(TimeSpan)方法在指定时间后自动取消
CancelAfter(int)方法在指定毫秒数后自动取消
Dispose()方法释放资源
CreateLinkedTokenSource()静态方法创建链接的取消源

CancellationToken 主要成员

成员类型说明
IsCancellationRequested属性是否已请求取消
CanBeCanceled属性此令牌能否被取消
WaitHandle属性获取等待取消信号的句柄
None静态属性空令牌(无法取消)
ThrowIfCancellationRequested()方法如果已取消则抛出异常
Register(Action)方法注册取消回调
Equals()方法比较两个令牌
GetHashCode()方法获取哈希码

重要注意事项

  1. 资源释放:始终对 CancellationTokenSource 调用 Dispose

  2. 回调顺序:多个回调按注册顺序执行

  3. 线程安全:这些类型都是线程安全的

  4. 性能考虑:频繁检查 IsCancellationRequested 可能有性能影响

  5. 异常处理:OperationCanceledException 是预期的异常,应适当处理

http://www.dtcms.com/a/525005.html

相关文章:

  • 散列查找及性能分析的应用
  • 百日挑战之单词篇(第二天)
  • 香港首位范·克莱本国际钢琴大赛冠军 沈靖韬 签约环球音乐 即将推出全新独奏专辑
  • 网站托管工作室wordpress防止f12
  • 传播网站建设建筑涂料网站设计
  • 详解C++中的字符串流
  • 中建西部建设广通讯网站小程序制作需要多少钱一个
  • 石家庄有哪些公司可以做网站南通网站优化找哪家
  • 全面掌握PostgreSQL关系型数据库,pgAdmin 图形化客户端,笔记12
  • 怎么做地方门户网站桂林市区
  • (论文速读)开放词汇3D场景理解的掩蔽点-实体对比
  • 做网站是经典网站设计作品
  • C++进阶:继承
  • 网站 建设运行情况网站移动转换
  • 如何上传ftp网站程序普像工业设计网站
  • 做响应式网站的公司如何做与别人的网站一样的
  • pytorch下对各种超参调整效果
  • 做网站会遇到的问题title 镇江网站建设
  • 怎么做网站底部版权信息在哪可以接企业网站建设的活
  • 聊城网站建设服务好赣州网站制作较好的公司
  • 今日行情明日机会——20251024
  • pip常用命令
  • 杂志网站建设推广方案好的设计作品网站
  • 多语言外贸网站开发wordpress 谷歌地图
  • 简单的电商网站开发网站建设的本质
  • 建行官方网站首页黄页网络的推广
  • 【力扣Hot100】刷题日记
  • IROS 2025现场,触觉力反馈、数据手套遥操作机器人灵巧手平台系统解决方案
  • vivo官方网站进入短视频推广计划
  • 绵阳网站seo网站建设小结