CancellationTokenSource

发布于:2025-02-11 ⋅ 阅读:(36) ⋅ 点赞:(0)

CancellationTokenSource 是 .NET 中用于实现任务或异步操作的 取消机制 的一个核心类。通过它,我们可以向异步任务发送取消信号,从而安全地终止任务的执行,避免不必要的资源消耗。


核心概念

  1. CancellationTokenSource

    • 用于发出取消信号的类。
    • 可以通过它生成一个 CancellationToken,将其传递给任务或异步方法。
    • 当调用 Cancel 方法时,表示发出了取消信号,任务会根据此信号选择是否终止。
  2. CancellationToken

    • CancellationTokenSource 提供,用于监听取消信号。
    • 异步任务可以通过检查 IsCancellationRequested 属性或调用 ThrowIfCancellationRequested 方法来检测取消信号。

基本用法

示例代码:使用 CancellationTokenSource 取消任务

 

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

class Program
{
    static async Task Main(string[] args)
    {
        // 创建 CancellationTokenSource
        CancellationTokenSource cts = new CancellationTokenSource();

        // 获取 CancellationToken
        CancellationToken token = cts.Token;

        // 启动任务
        var task = Task.Run(() => DoWork(token), token);

        Console.WriteLine("Press Enter to cancel the operation...");
        Console.ReadLine();

        // 发送取消信号
        cts.Cancel();

        try
        {
            // 等待任务完成
            await task;
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Task was cancelled.");
        }
        finally
        {
            cts.Dispose(); // 释放资源
        }
    }

    static void DoWork(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            // 检查是否收到取消信号
            token.ThrowIfCancellationRequested();

            Console.WriteLine($"Working... {i}");
            Thread.Sleep(1000); // 模拟工作
        }
    }
}
输出示例
  1. 如果未取消任务:

Working... 0
Working... 1
Working... 2
...
Working... 9

 2.如果按下回车取消任务:

Working... 0
Working... 1
Working... 2
Task was cancelled.

核心功能和方法

CancellationTokenSource 方法
方法 描述
Cancel() 发出取消信号。所有与该 CancellationTokenSource 关联的 CancellationToken 会收到信号。
CancelAfter(int delay) 在指定的毫秒数后发出取消信号。
Dispose() 释放 CancellationTokenSource 使用的资源。
CancellationToken 属性和方法
属性/方法 描述
IsCancellationRequested 表示是否已请求取消操作。
ThrowIfCancellationRequested() 如果收到取消信号,则抛出 OperationCanceledException 异常。
Register(Action callback) 注册一个回调函数,当取消信号被发出时会调用此回调。

高级用法

1. 超时自动取消

可以通过 CancelAfter 方法设置超时,在指定时间后自动取消任务。

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

class Program
{
    static async Task Main(string[] args)
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        // 设置超时时间为 5 秒
        cts.CancelAfter(5000);

        try
        {
            await Task.Run(() => DoWork(cts.Token), cts.Token);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Task was cancelled due to timeout.");
        }
    }

    static void DoWork(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"Working... {i}");
            Thread.Sleep(1000); // 模拟工作
        }
    }
}

 输出示例

Working... 0
Working... 1
Working... 2
Working... 3
Working... 4
Task was cancelled due to timeout.

 

2. 多个任务共享取消信号

多个任务可以共享同一个 CancellationToken,当发出取消信号时,所有任务都会被取消。

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

class Program
{
    static async Task Main(string[] args)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        var task1 = Task.Run(() => DoWork("Task 1", token), token);
        var task2 = Task.Run(() => DoWork("Task 2", token), token);

        Console.WriteLine("Press Enter to cancel all tasks...");
        Console.ReadLine();

        cts.Cancel(); // 取消所有任务

        try
        {
            await Task.WhenAll(task1, task2);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("All tasks were cancelled.");
        }
    }

    static void DoWork(string name, CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"{name} is working... {i}");
            Thread.Sleep(1000); // 模拟工作
        }
    }
}

 

输出示例
  1. 如果未取消:

Task 1 is working... 0
Task 2 is working... 0
Task 1 is working... 1
Task 2 is working... 1
...

 2.如果取消:

 

Task 1 is working... 0
Task 2 is working... 0
Task 1 is working... 1
Task 2 is working... 1
All tasks were cancelled.
3. 嵌套的 CancellationTokenSource

可以创建多个 CancellationTokenSource,并将它们关联到一个父 CancellationTokenSource。当父级取消时,所有子级都将取消。

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

class Program
{
    static async Task Main(string[] args)
    {
        CancellationTokenSource parentCts = new CancellationTokenSource();
        CancellationTokenSource childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token);

        var task = Task.Run(() => DoWork(childCts.Token), childCts.Token);

        Console.WriteLine("Press Enter to cancel parent token...");
        Console.ReadLine();

        parentCts.Cancel(); // 取消父级,子级也会被取消

        try
        {
            await task;
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Task was cancelled.");
        }
    }

    static void DoWork(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"Working... {i}");
            Thread.Sleep(1000); // 模拟工作
        }
    }
}

 

输出示例
  1. 如果未取消

Working... 0
Working... 1
Working... 2
...

 2.如果取消:

Working... 0
Working... 1
Task was cancelled.

注意事项

  1. 取消是协作的

    • CancellationTokenSource 只是发出取消信号,任务本身需要主动响应取消(例如通过检查 IsCancellationRequested 或调用 ThrowIfCancellationRequested)。
    • 如果任务忽略了取消信号,它仍然会继续运行。
  2. Dispose 的使用

    • 使用完 CancellationTokenSource 后,应该调用 Dispose 方法以释放资源,特别是在频繁创建和销毁时。
  3. 异常处理

    • ThrowIfCancellationRequested 被调用时,会抛出 OperationCanceledException。这种异常通常用于标识任务的正常取消,而不是错误。
  4. 线程安全

    • CancellationTokenCancellationTokenSource 是线程安全的,可以在多个线程之间安全地共享。

总结

  • CancellationTokenSourceCancellationToken 提供了优雅的任务取消机制
  • 它们适用于长时间运行的任务、异步操作或需要动态中断的场景。
  • 通过合理使用 CancellationTokenSourceCancellationToken,可以让你的程序更加高效,避免无谓的资源浪费。

网站公告

今日签到

点亮在社区的每一天
去签到