编辑
2025-10-13
C#
00

目录

CancellationTokenSource 基本概念
基本使用场景
网络请求取消
长时间计算取消
高级应用场景
并行操作取消
链接多个 CancellationTokenSource
注意
常见陷阱
结论

在现代异步编程里,CancellationTokenSource`是一个既强大又灵活的工具,能帮助我们管理并取消耗时较长的任务。接下来,我们会详细聊聊它的使用场景、一些推荐的做法,以及实际的例子。

CancellationTokenSource 基本概念

CancellationTokenSource 是 .NET 中用于协作式取消操作的核心类。它允许您:

  • 创建可取消的操作
  • 发送取消信号
  • 优雅地中断长时间运行的任务
  • 管理资源释放

基本使用场景

网络请求取消

C#
namespace AppCancellation { internal class Program { private static async Task<string> DownloadWebContentAsync(string url, CancellationToken cancellationToken) { using (var client = new HttpClient()) { try { // 设置超时时间 client.Timeout = TimeSpan.FromSeconds(10); // 发起异步GET请求,并传入取消令牌 var response = await client.GetAsync(url, cancellationToken); // 确保请求成功 response.EnsureSuccessStatusCode(); // 读取响应内容 return await response.Content.ReadAsStringAsync(cancellationToken); } catch (OperationCanceledException) { // 捕获操作取消异常 Console.WriteLine("下载已被取消"); return string.Empty; } } } static async Task Main(string[] args) { // 创建取消令牌源 using (var cts = new CancellationTokenSource()) { // 可以在另一个线程中取消操作 _ = Task.Run(() => { // 例如3秒后取消 Thread.Sleep(3000); cts.Cancel(); }); try { string result = await DownloadWebContentAsync("http://blog.idiosoft.com", cts.Token); Console.WriteLine(result); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } } } }

image.png

长时间计算取消

C#
namespace AppCancellation { internal class Program { private static async Task<List<int>> ComputePrimesAsync(int max, CancellationToken cancellationToken) { var primes = new List<int>(); for (int i = 2; i <= max; i++) { // 定期检查取消状态 cancellationToken.ThrowIfCancellationRequested(); if (IsPrime(i)) { primes.Add(i); // 模拟复杂计算 await Task.Delay(10, cancellationToken); } } return primes; } // 判断是否为质数的辅助方法 private static bool IsPrime(int number) { if (number < 2) return false; for (int i = 2; i <= Math.Sqrt(number); i++) { if (number % i == 0) return false; } return true; } static async Task Main(string[] args) { var cancellationTokenSource = new CancellationTokenSource(); try { // 设置超时取消 cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(1)); var result = await ComputePrimesAsync( 10000, cancellationTokenSource.Token ); Console.WriteLine($"Computed {result.Count} primes"); } catch (OperationCanceledException) { Console.WriteLine("计算被取消"); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } } }

高级应用场景

并行操作取消

C#
namespace AppCancellation { internal class Program { private static async Task ProcessMultipleTasksAsync() { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); try { var tasks = new List<Task> { Task.Run(() => LongRunningTask1(cts.Token)), Task.Run(() => LongRunningTask2(cts.Token)), Task.Run(() => LongRunningTask3(cts.Token)) }; await Task.WhenAll(tasks); } catch (OperationCanceledException) { Console.WriteLine("某些任务被取消"); } } private static async Task LongRunningTask1(CancellationToken token) { try { for (int i = 0; i < 10; i++) { // 模拟耗时操作 await Task.Delay(1000, token); // 检查是否取消 token.ThrowIfCancellationRequested(); Console.WriteLine($"任务1:第 {i + 1} 次迭代"); } } catch (OperationCanceledException) { Console.WriteLine("任务1被取消"); throw; } } private static async Task LongRunningTask2(CancellationToken token) { try { for (int i = 0; i < 10; i++) { // 模拟耗时操作 await Task.Delay(1000, token); // 检查是否取消 token.ThrowIfCancellationRequested(); Console.WriteLine($"任务2:第 {i + 1} 次迭代"); } } catch (OperationCanceledException) { Console.WriteLine("任务2被取消"); throw; } } private static async Task LongRunningTask3(CancellationToken token) { try { for (int i = 0; i < 10; i++) { // 模拟耗时操作 await Task.Delay(1000, token); // 检查是否取消 token.ThrowIfCancellationRequested(); Console.WriteLine($"任务3:第 {i + 1} 次迭代"); } } catch (OperationCanceledException) { Console.WriteLine("任务3被取消"); throw; } } static async Task Main(string[] args) { await ProcessMultipleTasksAsync(); } } }

image.png

  • 启动3个并行任务
  • 5秒后自动取消所有任务
  • 捕获并处理取消异常
  • 打印任务运行和取消的相关信息

链接多个 CancellationTokenSource

C#
namespace AppCancellation { internal class Program { private static async Task ComplexCancellationScenario() { using var primaryCts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); using var secondaryCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // 链接两个 Token var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( primaryCts.Token, secondaryCts.Token ); try { await LongRunningOperation(linkedCts.Token); } catch (OperationCanceledException) { Console.WriteLine("操作被取消"); } finally { linkedCts.Dispose(); } } private static async Task LongRunningOperation(CancellationToken cancellationToken) { try { for (int i = 0; i < 20; i++) { // 模拟一个长时间运行的操作 await Task.Delay(1000, cancellationToken); Console.WriteLine($"正在执行操作,已运行 {i + 1} 秒"); // 检查是否已请求取消 cancellationToken.ThrowIfCancellationRequested(); } } catch (TaskCanceledException) { Console.WriteLine("任务被取消"); throw; } } static async Task Main(string[] args) { await ComplexCancellationScenario(); Console.ReadLine(); } } }

image.png

注意

  • 始终使用 using 语句管理 CancellationTokenSource
  • 频繁检查 cancellationToken.IsCancellationRequested
  • 优雅处理 OperationCanceledException
  • 避免过度使用取消机制
  • 正确传播取消信号
  • CancellationTokenSource 开销很低
  • 推荐用于耗时操作
  • 不适合非常短的同步方法

常见陷阱

  • 忘记检查取消状态
  • 未正确传播取消 Token
  • 没有正确处理 OperationCanceledException

结论

CancellationTokenSource 是现代 .NET 异步编程中不可或缺的工具。通过正确使用,可以显著提高应用程序的响应性和资源管理能力。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!