16、取消线程

取消线程

知识点概述

线程取消是多线程编程中的重要概念,用于优雅地停止正在执行的线程。.NET提供了多种取消线程的方式:

  • CancellationToken: 现代推荐的取消机制(.NET 4.0+)
  • Thread.Abort(): 强制终止线程(已过时,不推荐)
  • Thread.Interrupt(): 中断等待中的线程
  • 共享标志位: 使用bool变量控制线程退出
  • ManualResetEvent: 基于事件的取消机制

代码案例

案例1:使用CancellationToken取消线程(推荐方式)

using System;
using System.Threading;
using System.Threading.Tasks;

class CancellationTokenDemo
{
    static async Task Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        
        // 启动可取消的任务
        Task longRunningTask = LongRunningOperationAsync(cts.Token);
        
        Console.WriteLine("任务已启动,按任意键取消...");
        Console.ReadKey();
        
        // 请求取消
        cts.Cancel();
        
        try
        {
            await longRunningTask;
            Console.WriteLine("任务正常完成");
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("任务已取消");
        }
        
        cts.Dispose();
    }
    
    static async Task LongRunningOperationAsync(CancellationToken cancellationToken)
    {
        for (int i = 0; i < 100; i++)
        {
            // 检查取消请求
            cancellationToken.ThrowIfCancellationRequested();
            
            Console.WriteLine($"处理项目 {i + 1}/100");
            await Task.Delay(200, cancellationToken);
        }
        
        Console.WriteLine("操作完成");
    }
}

案例2:超时自动取消

using System;
using System.Threading;
using System.Threading.Tasks;

class TimeoutCancellationDemo
{
    static async Task Main()
    {
        // 设置5秒超时
        using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
        {
            try
            {
                await SimulateWorkAsync(cts.Token);
                Console.WriteLine("工作在超时前完成");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("工作因超时被取消");
            }
        }
        
        Console.WriteLine("\n演示手动取消与超时的组合:");
        await CombinedCancellationDemo();
    }
    
    static async Task SimulateWorkAsync(CancellationToken cancellationToken)
    {
        for (int i = 0; i < 30; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            
            Console.WriteLine($"工作进度: {i + 1}/30");
            await Task.Delay(300, cancellationToken);
        }
    }
    
    static async Task CombinedCancellationDemo()
    {
        using (CancellationTokenSource timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
        using (CancellationTokenSource manualCts = new CancellationTokenSource())
        {
            // 组合超时取消和手动取消
            using (CancellationTokenSource combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
                timeoutCts.Token, manualCts.Token))
            {
                Task workTask = SimulateWorkAsync(combinedCts.Token);
                
                // 模拟2秒后手动取消
                _ = Task.Run(async () =>
                {
                    await Task.Delay(2000);
                    manualCts.Cancel();
                    Console.WriteLine("手动取消请求已发送");
                });
                
                try
                {
                    await workTask;
                }
                catch (OperationCanceledException)
                {
                    if (timeoutCts.Token.IsCancellationRequested)
                        Console.WriteLine("因超时取消");
                    else if (manualCts.Token.IsCancellationRequested)
                        Console.WriteLine("因手动请求取消");
                }
            }
        }
    }
}

案例3:使用共享标志位取消线程

using System;
using System.Threading;

class FlagBasedCancellation
{
    private static volatile bool _shouldStop = false;
    
    static void Main()
    {
        Thread workerThread = new Thread(DoWork);
        workerThread.Start();
        
        Console.WriteLine("线程已启动,按任意键停止...");
        Console.ReadKey();
        
        // 设置停止标志
        _shouldStop = true;
        
        // 等待线程完成
        workerThread.Join();
        Console.WriteLine("线程已停止");
    }
    
    static void DoWork()
    {
        int counter = 0;
        
        while (!_shouldStop)
        {
            Console.WriteLine($"工作中... {++counter}");
            Thread.Sleep(500);
        }
        
        Console.WriteLine($"线程收到停止信号,共执行了 {counter} 次操作");
    }
}

案例4:使用Thread.Interrupt中断等待

using System;
using System.Threading;

class ThreadInterruptDemo
{
    static void Main()
    {
        Thread workerThread = new Thread(WaitingWork);
        workerThread.Start();
        
        Console.WriteLine("线程正在等待,3秒后中断...");
        Thread.Sleep(3000);
        
        // 中断线程
        workerThread.Interrupt();
        workerThread.Join();
        
        Console.WriteLine("程序结束");
    }
    
    static void WaitingWork()
    {
        try
        {
            Console.WriteLine("开始长时间等待...");
            Thread.Sleep(10000); // 等待10秒
            Console.WriteLine("等待完成");
        }
        catch (ThreadInterruptedException)
        {
            Console.WriteLine("等待被中断,开始清理工作...");
            // 执行清理工作
            Thread.Sleep(1000);
            Console.WriteLine("清理完成");
        }
    }
}

案例5:优雅的取消处理和资源清理

using System;
using System.Threading;
using System.Threading.Tasks;
using System.IO;

class GracefulCancellationDemo
{
    static async Task Main()
    {
        using (CancellationTokenSource cts = new CancellationTokenSource())
        {
            // 设置取消回调
            cts.Token.Register(() => Console.WriteLine("取消操作已触发"));
            
            Task processTask = ProcessFilesAsync(cts.Token);
            
            Console.WriteLine("文件处理已开始,按任意键取消...");
            Console.ReadKey();
            
            cts.Cancel();
            
            try
            {
                await processTask;
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("文件处理被取消");
            }
        }
    }
    
    static async Task ProcessFilesAsync(CancellationToken cancellationToken)
    {
        try
        {
            for (int i = 1; i <= 10; i++)
            {
                using (cancellationToken.Register(() => Console.WriteLine($"正在取消文件 {i} 的处理...")))
                {
                    await ProcessSingleFileAsync(i, cancellationToken);
                }
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("开始清理已处理的文件...");
            await CleanupAsync();
            throw; // 重新抛出以通知调用者
        }
    }
    
    static async Task ProcessSingleFileAsync(int fileNumber, CancellationToken cancellationToken)
    {
        Console.WriteLine($"开始处理文件 {fileNumber}");
        
        // 模拟文件处理,支持取消
        for (int step = 1; step <= 5; step++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            
            Console.WriteLine($"  文件 {fileNumber} - 步骤 {step}/5");
            await Task.Delay(800, cancellationToken);
        }
        
        Console.WriteLine($"文件 {fileNumber} 处理完成");
    }
    
    static async Task CleanupAsync()
    {
        Console.WriteLine("执行清理操作...");
        await Task.Delay(1000); // 模拟清理时间
        Console.WriteLine("清理完成");
    }
}

案例6:多线程取消协调

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

class MultiThreadCancellationDemo
{
    static async Task Main()
    {
        using (CancellationTokenSource cts = new CancellationTokenSource())
        {
            // 启动多个工作任务
            List<Task> tasks = new List<Task>();
            for (int i = 1; i <= 3; i++)
            {
                int workerId = i;
                tasks.Add(WorkerTaskAsync(workerId, cts.Token));
            }
            
            Console.WriteLine("所有工作线程已启动,按任意键取消所有任务...");
            Console.ReadKey();
            
            Console.WriteLine("发送取消信号...");
            cts.Cancel();
            
            // 等待所有任务响应取消
            try
            {
                await Task.WhenAll(tasks);
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("所有任务已取消");
            }
        }
    }
    
    static async Task WorkerTaskAsync(int workerId, CancellationToken cancellationToken)
    {
        try
        {
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();
                
                Console.WriteLine($"工作线程 {workerId} 正在执行...");
                await Task.Delay(1000, cancellationToken);
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine($"工作线程 {workerId} 收到取消信号,正在清理...");
            // 模拟清理工作
            await Task.Delay(500);
            Console.WriteLine($"工作线程 {workerId} 清理完成");
            throw;
        }
    }
}

知识点总结

  1. CancellationToken: 现代.NET推荐的取消机制,提供协作式取消
  2. 超时取消: 可以设置自动超时,避免长时间运行的任务
  3. 组合取消: 可以组合多个取消源,灵活控制取消条件
  4. 协作式取消: 线程需要主动检查取消标记,不是强制终止
  5. 资源清理: 取消时要确保正确清理资源,避免内存泄漏
  6. 异常处理: OperationCanceledException是取消操作的标准异常
  7. 性能考虑: 适当的取消检查频率平衡响应性和性能
posted @ 2025-08-27 01:24  jessqiu  阅读(30)  评论(0)    收藏  举报