C# 异步编程中 CancellationToken 与 CancellationTokenSource 的用法

400 阅读4分钟

前言

在异步编程中,任务取消机制是不可或缺的一部分。随着 .NET Core 对异步编程的全面支持,CancellationToken 成为了控制异步操作生命周期的重要工具。

本文将系统性地讲解 CancellationTokenCancellationTokenSource 的基本用法、工作原理以及常见使用场景,帮助大家更好地掌握异步任务的取消机制。

一、CancellationToken 简介

CancellationToken 是 .NET 提供的一个轻量级结构体,用于通知一个或多个线程应该停止当前操作。它本身并不执行取消操作,而是作为一个信号传递机制。

构造函数
public CancellationToken(); 
// 默认构造函数(不常用)
public CancellationToken(bool canceled); 
// 初始化为取消状态或非取消状态

常用属性

属性描述
None获取一个空的 CancellationToken
CanBeCanceled表示是否可以被取消
IsCancellationRequested表示是否已请求取消
WaitHandle获取与此令牌关联的 WaitHandle

注册回调方法

通过 Register 方法可以注册回调函数,在取消时执行:

public CancellationTokenRegistration Register(Action callback);
public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);
public CancellationTokenRegistration Register([NullableAttribute(new[] { 1, 2 })] Action<object?> callback, object? state);
public CancellationTokenRegistration Register([NullableAttribute(new[] { 1, 2 })] Action<object?> callback, object? state, bool useSynchronizationContext);

注意:每个注册的回调只会执行一次。

取消注册

通过返回的 CancellationTokenRegistration 可以取消注册:

var registration = token.Register(() => Console.WriteLine("Canceled"));
registration.Unregister();

二、CancellationTokenSource 控制器

CancellationTokenSourceCancellationToken 的控制器,通过它来触发取消操作。

创建方式

CancellationTokenSource cts = new CancellationTokenSource();

也可以指定延迟时间自动取消:

cts.CancelAfter(5000); // 5秒后自动取消

主要方法

  • Cancel():立即取消

  • Cancel(bool throwOnFirstException):指定异常处理策略

  • CancelAfter(int millisecondsDelay) / CancelAfter(TimeSpan delay):延迟取消

异常处理行为

当多个回调抛出异常时,可以通过 throwOnFirstException 参数控制处理方式:

  • true:遇到第一个异常立即抛出,后续不再执行。

  • false:收集所有异常并抛出 AggregateException

示例:

try {
    cts.Token.Register(() => throw new Exception("1"));
    cts.Token.Register(() => throw new Exception("2"));
    cts.Cancel(false); // 抛出 AggregateException
} catch (Exception ex) {
    // ex is AggregateException: [Exception("2"), Exception("1")]
}

三、CancellationToken 的组合使用

通过 CreateLinkedTokenSource 可以创建一个联动的 CancellationToken

CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);

当任意一个源取消时,联动的 CancellationToken 也会被取消。

典型使用场景

场景一:取消异步任务

CancellationTokenSource cts = new CancellationTokenSource();
var task = new Task(() =>
{
    Thread.Sleep(1500);
    Console.WriteLine("Execute Some Code");
}, cts.Token);

task.Start();
cts.Cancel();
Thread.Sleep(1000);
Console.WriteLine("Task状态:" + task.Status); // Canceled

场景二:资源释放后通知

class Demo
{
    CancellationTokenSource cts = new CancellationTokenSource();

    public CancellationToken Token => cts.Token;

    public void Close()
    {
        Thread.Sleep(3000);
        cts.Cancel(); // 执行通知
    }
}

调用方无需关心何时注册回调。

场景三:无限循环任务控制

public CancellationTokenSource Listen()
{
    var cts = new CancellationTokenSource();

    Task.Run(() =>
    {
        while (true)
        {
            cts.Token.ThrowIfCancellationRequested();
            Thread.Sleep(1000);
            Console.WriteLine("Run");
        }
    });

    return cts;
}

外部可通过 cts.Cancel() 终止循环。

场景四:作为方法参数传递取消信号

static void Main(string[] args)
{
    var cts = new CancellationTokenSource();
    Method("hello", cts.Token);
    cts.Cancel();
}

static void Method(string message, CancellationToken cancellationToken = default)
{
    Console.WriteLine($"message:{message}");
    cancellationToken.Register(() => Console.WriteLine("complete"));
}

这种方式使方法具备可取消性,且不影响主流程逻辑。

总结

CancellationTokenCancellationTokenSource 是 .NET Core 中实现异步任务取消的核心组件。

它们提供一种优雅、可控的方式来管理异步操作的生命周期,尤其适用于需要动态控制任务执行状态的场景。通过合理使用这些组件,可以显著提升代码的响应性和健壮性。

在实际开发中,建议优先使用 CancellationTokenSource 来创建和管理 CancellationToken,并通过 RegisterThrowIfCancellationRequested 方法进行精细控制。同时,结合多线程、事件驱动等模式,可以构建更加灵活的应用架构。

关键词

CancellationToken、CancellationTokenSource、异步编程、Task、取消操作、Register、Unregister、ThrowIfCancellationRequested、CreateLinkedTokenSource、异步任务控制

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者: 没有星星的夏季 

出处:cnblogs.com/shanfeng1000/p/13402152.html

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!