线程任务取消——CancellationToken

在多线程环境下,CancellationToken 是 .NET 中用于协作式取消操作的标准机制。

CancellationTokenSource:这是“发布者”。它负责在需要取消时发出信号(调用 .Cancel())。
CancellationToken:这是“订阅者”或“载体”。它是一个轻量级的结构体,被传递给各个任务或线程。

在多线程/Task中,token主要有三种使用方式:

1、注册回调

  注册一个委托,当取消被请求时,该委托会被执行。

  常用于释放非托管资源或中断阻塞操作

 CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
            CancellationToken token = _cancellationTokenSource.Token; 
// 注册一个回调,当取消发生时执行
token.Register(() => {
    Console.WriteLine("取消请求已收到,正在清理资源...");
    // 在这里执行清理逻辑,例如关闭流、释放句柄等
});

2、轮询检查(IsCancellationRequested)

  常用于长时间运行的循环中

Task.Run(() => {
    for (int i = 0; i < 1000; i++) {
        // 1. 检查是否请求了取消
        if (token.IsCancellationRequested) {
            Console.WriteLine("任务被取消,正在退出循环。");
            // 2. 优雅地退出,或者抛出异常
            token.ThrowIfCancellationRequested(); 
        }

        // 模拟工作
        Console.WriteLine($"处理中: {i}");
        Thread.Sleep(100);
    }
}, token); // 注意:将 token 传递给 Task.Run 本身也有助于在取消时阻止任务开始

3、传递给异步API

许多 .NET 异步方法(如 Stream.ReadAsync, HttpClient.GetAsync)都接受 CancellationToken 作为参数。这允许这些底层操作在收到取消信号时立即停止 I/O 操作,而不是等待完成。

// 假设这是一个耗时的网络请求
try {
    // 将 token 传递给异步方法
    var result = await httpClient.GetAsync("https://example.com", token);
}
catch (OperationCanceledException) {
    Console.WriteLine("网络请求已被取消。");
}

4、使用Demo

using System; 
using System.Threading;

using
System.Threading.Tasks; namespace CancellationTokenDemo { // 这里是你提供的 Worker 类定义 public class Worker { private CancellationTokenSource _cancellationTokenSource; public void StartWork() { _cancellationTokenSource = new CancellationTokenSource(); CancellationToken token = _cancellationTokenSource.Token; Task.Run(() => { try { Console.WriteLine("🚀 任务开始运行..."); for (int i = 0; i < 10; i++) { // 关键点:在循环中检查是否请求了取消 token.ThrowIfCancellationRequested(); Console.WriteLine($"⏳ 正在处理... {i * 10}%"); // 模拟耗时操作 (这里使用 Thread.Sleep 模拟阻塞) // 在实际异步代码中,通常使用 Task.Delay(..., token) Thread.Sleep(500); } Console.WriteLine("✅ 工作正常完成。"); } catch (OperationCanceledException) { // 捕获取消异常,进行清理或记录日志 Console.WriteLine("🛑 捕获到取消请求,任务已停止。"); } }, token); } public void StopWork() { // 触发取消信号 // 注意:这里不需要 Dispose,通常由拥有者管理生命周期 if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested) { Console.WriteLine("\n📤 正在调用 StopWork 发送取消信号..."); _cancellationTokenSource.Cancel(); } } // 建议实现 IDisposable 模式或在程序退出时调用 public void Cleanup() { _cancellationTokenSource?.Dispose(); Console.WriteLine("🧹 资源已释放。"); } } class Program { static void Main(string[] args) { Console.WriteLine("=== C# CancellationToken 演示 ==="); // 1. 实例化 Worker Worker worker = new Worker(); // 2. 启动后台任务 worker.StartWork(); // 3. 模拟主线程等待用户输入 // 在实际场景中,这里可能是服务器监听、UI 事件循环等 Console.WriteLine("按任意键取消任务,或等待任务自动完成..."); Console.ReadKey(); // 4. 触发取消 worker.StopWork(); // 5. 给一点时间让后台任务处理取消信号并退出 // (在实际生产代码中,通常会 await Task.WhenAny(...) 或 .Wait()) Thread.Sleep(1000); // 6. 清理资源 worker.Cleanup(); Console.WriteLine("程序结束。按任意键退出。"); Console.ReadKey(); } } }

 

posted @ 2026-04-21 11:31  echo-efun  阅读(4)  评论(0)    收藏  举报