C#如何正确的停止一个多线程Task?CancellationTokenSource 的用法。

在 C# 中停止一个 Task 需要谨慎处理,因为直接强制终止(如 Thread.Abort())会导致资源泄漏或状态不一致。推荐使用 ​​协作式取消(Cooperative Cancellation)​ ​ 通过 CancellationToken 实现安全停止。以下是详细方法:


​1. 使用 CancellationToken 取消 Task​

​(1) 创建 CancellationTokenSource​
复制代码

csharp

复制代码
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
​(2) 在 Task 中检查取消请求​
复制代码

csharp

复制代码
Task longRunningTask = Task.Run(() =>
{
    while (true)
    {
        // 检查是否被取消
        if (token.IsCancellationRequested)
        {
            Console.WriteLine("Task 已取消");
            token.ThrowIfCancellationRequested(); // 抛出 OperationCanceledException
        }

        // 模拟工作
        Console.WriteLine("Working...");
        Thread.Sleep(1000);
    }
}, token);
​(3) 触发取消​
复制代码

csharp

复制代码
// 请求取消
cts.Cancel();

// 可选:等待Task处理取消(避免资源泄漏)
try
{
    await longRunningTask;
}
catch (OperationCanceledException)
{
    Console.WriteLine("Task 已安全终止");
}

​2. 处理取消时的清理工作​

如果 Task 需要在取消时释放资源(如关闭文件、数据库连接),通过 finally 块或 Register 方法实现:

复制代码

csharp

复制代码
Task.Run(() =>
{
    using (var resource = new SomeDisposableResource())
    {
        try
        {
            while (!token.IsCancellationRequested)
            {
                // 工作代码
            }
        }
        finally
        {
            Console.WriteLine("释放资源");
        }
    }
}, token);

或通过 CancellationToken.Register

复制代码

csharp

复制代码
token.Register(() =>
{
    Console.WriteLine("取消时执行清理");
});

​3. 超时自动取消​

复制代码

csharp

复制代码
// 设置 5秒超时
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
Task.Run(() => { /* ... */ }, cts.Token);

​4. 为什么不推荐强制终止 Task?​

  • Thread.Abort() / Task.Dispose()

    • ❌ 抛出 ThreadAbortException,可能导致锁未释放、文件未关闭等问题。
    • ❌ .NET Core 及以上版本已移除 Thread.Abort()
  • ​协作式取消的优势​

    • ✅ 安全:Task 有机会清理资源。
    • ✅ 可控:可通过异常捕获取消逻辑。

​5. 完整示例​

复制代码

csharp

复制代码
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var cts = new CancellationTokenSource();
        var token = cts.Token;

        Task task = Task.Run(() =>
        {
            try
            {
                while (true)
                {
                    token.ThrowIfCancellationRequested();
                    Console.WriteLine("Working...");
                    Thread.Sleep(1000);
                }
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("任务已取消");
            }
        }, token);

        // 3秒后取消
        Thread.Sleep(3000);
        cts.Cancel();

        await task; // 等待任务处理取消
    }
}

​总结​

  • ​推荐方式​ :始终通过 CancellationToken 实现协作式取消。
  • ​强制终止​:仅在极端情况下考虑,并确保资源释放。
  • ​超时控制​ :结合 CancellationTokenSource(TimeSpan) 实现自动取消。

这种方法适用于 Taskasync/awaitParallel.ForEach 等场景,是 .NET 中处理任务取消的标准模式。

相关推荐
阿猿收手吧!几秒前
【C++】异步编程:std::async终极指南
开发语言·c++
小程故事多_8014 分钟前
Agent Infra核心技术解析:Sandbox sandbox技术原理、选型逻辑与主流方案全景
java·开发语言·人工智能·aigc
沐知全栈开发15 分钟前
SQL 日期处理指南
开发语言
黎雁·泠崖18 分钟前
【魔法森林冒险】3/14 Allen类(一):主角核心属性与初始化
java·开发语言
黎雁·泠崖22 分钟前
【魔法森林冒险】1/14 项目总览:用Java打造你的第一个回合制冒险游戏
java·开发语言
独好紫罗兰27 分钟前
对python的再认识-基于数据结构进行-a006-元组-拓展
开发语言·数据结构·python
C++ 老炮儿的技术栈32 分钟前
Qt 编写 TcpClient 程序 详细步骤
c语言·开发语言·数据库·c++·qt·算法
yuuki23323342 分钟前
【C++】继承
开发语言·c++·windows
222you42 分钟前
Redis的主从复制和哨兵机制
java·开发语言
牛奔1 小时前
如何理解 Go 的调度模型,以及 G / M / P 各自的职责
开发语言·后端·golang