C# 异步编程技巧 Task 和 CancellationTokenSource 的妙用

168 阅读3分钟

前言

在程序开发中异步编程和任务管理是确保高效、响应式用户体验的关键。C# 提供强大的工具来处理这些需求,其中 TaskCancellationTokenSource 是两个非常重要的组件。Task 用于管理和协调异步操作,而 CancellationTokenSource 则提供优雅的方式来取消或中断这些操作。

在.NET中,CancellationTokenSourceCancellationTokenTask是处理异步操作和取消任务的重要工具。本文将通过一些简单的例子,帮助大家理解它们的用法和协作方式。

正文

CancellationTokenSource

CancellationTokenSource是 C# 中用于生成和管理 CancellationToken 的类,它充当取消操作的触发器。

通过 CancellationTokenSource,开发者可以控制取消信号的发出,并将这些信号传递给多个异步任务或长时间运行的操作,以便它们能够响应取消请求并优雅地终止。

常用属性和方法

属性

Token: 返回与此源关联的 CancellationToken。每个 CancellationTokenSource 实例都可以生成一个唯一的 CancellationToken,该令牌可以传递给多个消费者以监听取消事件。

方法

Cancel(): 触发取消操作。调用此方法后,所有与该 CancellationTokenSource 关联的 CancellationToken 将进入已取消状态,通知所有监听者执行取消逻辑。

CancelAfter(int millisecondsDelay): 设置一个计时器,在指定的毫秒数后自动触发取消操作。这对于设置超时非常有用,确保长时间运行的任务不会无限期挂起。

Dispose(): 释放由 CancellationTokenSource 占用的资源。调用此方法后,不能再使用该实例触发取消操作。建议在不再需要 CancellationTokenSource 时调用 Dispose(),以避免资源泄漏。

示例
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task.Run(() => {
    for (int i = 0; i < 10; i++)
    {
        if (token.IsCancellationRequested)
        {
            Console.WriteLine("Task canceled");
            break;
        }
        Console.WriteLine($"Task running: {i}");
        Thread.Sleep(500);
    }
});

Thread.Sleep(2000);
cts.Cancel();

CancellationToken

CancellationToken 是一个轻量级结构,用于传播取消请求。它由 CancellationTokenSource 生成,并可以传递给多个消费者(如异步任务或长时间运行的操作),以便它们能够监听和响应取消事件。通过这种方式,开发者可以在需要时优雅地终止这些操作,确保应用程序的高效性和响应性。

常用属性和方法

属性

IsCancellationRequested: 获取一个布尔值,指示是否已发出取消请求。此属性可以定期检查以确定是否应该停止当前操作。

方法

ThrowIfCancellationRequested(): 如果已请求取消,则抛出 OperationCanceledException。此方法提供了一种简便的方式来立即响应取消请求,并且可以在代码的关键位置调用以确保及时处理取消信号。

Register(Action callback, bool useSynchronizationContext = true): 注册一个回调函数,在取消请求触发时执行。callback 参数是一个无参数的动作(Action),而 useSynchronizationContext 参数控制回调是否在同步上下文中执行,默认为 true。这对于确保回调在正确的线程上执行非常重要,尤其是在 UI 应用程序中。

示例
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task.Run(() => {
    token.Register(() => Console.WriteLine("Cancellation registered"));

    try
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"Task running: {i}");
            Thread.Sleep(500);
        }
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Task was canceled");
    }
});

Thread.Sleep(2000);
cts.Cancel();

Task与CancellationToken

Task 是.NET中的异步操作单元。结合CancellationToken可以在任务运行时取消它。

尽早检查取消请求:在长时间运行的任务中,尽早检查 IsCancellationRequested 或调用 ThrowIfCancellationRequested(),以便及时响应取消请求。

避免不必要的装箱操作:由于 CancellationToken 是一个结构体,尽量避免不必要的装箱操作,以保持性能。

合理设计取消逻辑:确保取消逻辑是幂等的,即多次调用取消请求不会产生副作用,并且任务能够在任何点安全地中止。

示例:取消任务
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task task = Task.Run(() => {
    for (int i = 0; i < 10; i++)
    {
        if (token.IsCancellationRequested)
        {
            Console.WriteLine("Task canceled");
            break;
        }
        Console.WriteLine($"Task running: {i}");
        Thread.Sleep(500);
    }
}, token);

Thread.Sleep(2000);
cts.Cancel();

try
{
    task.Wait();
}
catch (AggregateException ex)
{
    foreach (var inner in ex.InnerExceptions)
    {
        if (inner is TaskCanceledException)
        {
            Console.WriteLine("Task cancellation exception caught");
        }
    }
}
示例:带超时的任务
var cts = new CancellationTokenSource(3000); // 3秒后自动取消
CancellationToken token = cts.Token;

Task.Run(() => {
    try
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine($"Task running: {i}");
            Thread.Sleep(1000);
        }
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Task canceled due to timeout");
    }
});

总结

1、使用CancellationTokenSource来控制取消。

2、通过CancellationToken将取消信号传递给任务或方法。

3、任务中可以通过ThrowIfCancellationRequested或检查IsCancellationRequested响应取消请求。

4、合理使用Register可以处理取消时的回调逻辑。

通过灵活运用这些工具,你可以编写更高效、可控的异步程序。

本文探讨了 TaskCancellationToken 的技术细节,还分享了许多实用的优化技巧。希望这些内容能够帮助大家更好地理解和应用这两个强大的工具,在未来的项目中实现更加高效和可靠的异步编程。

最后

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

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

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

作者:chenyishi

出处:cnblogs.com/chenyishi/p/18620273

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