问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深入了解C#的CancellationToken

创作时间:
作者:
@小白创作中心

深入了解C#的CancellationToken

引用
CSDN
1.
https://blog.csdn.net/farway000/article/details/138174167

CancellationToken是C#中用于实现异步操作取消的重要机制。本文将深入探讨CancellationToken的工作原理、使用方法以及最佳实践,帮助开发者更好地理解和应用这一机制。

CancellationToken概述

在异步编程中,长时间运行的操作需要一种机制来控制其执行流程。CancellationToken提供了一种轻量级的对象,用于协作式取消异步操作。它允许创建者向所有使用该令牌的操作发送取消请求,而每个操作则负责监听并响应这个请求。

CancellationToken的基本心智模型

  • 一个对象负责创建取消令牌,并将其传递给所有需要监听取消请求的操作。
  • 每个操作可以将这个令牌的副本传递给其他操作。
  • 创建令牌的对象可以使用该令牌请求操作停止。
  • 只有创建者可以发起取消请求,操作本身不能取消自己。
  • 每个监听器都有责任注意到取消请求并做出响应。

.NET如何实现CancellationToken

.NET通过两个类来实现取消机制:

  • CancellationTokenSource:负责创建取消令牌并向所有副本发送取消请求。
  • CancellationToken:用于监听令牌的状态。

此外,还涉及OperationCancelledException类型,用于验证取消的来源并通知其他组件。

使用CancellationToken的示例

下面是一个使用CancellationToken的示例代码:

public async Task CancellableMethod()
{
    var tokenSource = new CancellationTokenSource();
    // Queue some long running tasks
    for(int i = 0;i < 10;++i)
    {
        Task.Run(() => DoSomeWork(tokenSource.Token), tokenSource.Token);
    }
    // After some delay/when you want manual cancellation
    tokenSource.Cancel();
}

// Runs on a different thread
public async Task DoSomeWork(CancellationToken ct)
{
    int maxIterations = 100;
    for(int i = 0;i < maxIterations;++i)
    {
        // Do some long running work
        if(ct.IsCancellationRequested)
        {
            Console.WriteLine("Task cancelled.");
            ct.ThrowIfCancellationRequested();
        }
    }
}

如何监听和响应取消请求

可取消操作需要确定如何正常终止并响应取消请求。通常,这包括必要的清理工作。在更复杂的情况下,可以使用ThrowIfCancellationRequested方法抛出OperationCanceledException

监听取消请求的机制

通过轮询监听

public static void SomeLongRunningOperation(CancellationToken ct)
{
    while(!ct.IsCancellationRequested)
    {
        DoWork(); // perform one unit of work
    }
    // perform cleanup if needed
}

通过注册回调监听

public static void DownloadSomeHugeFile(CancellationToken ct)
{
    WebClient wc = new WebClient();
    ct.Register(() =>
    {
        wc.CancelAsync();
    });
    wc.DownloadStringAsync("https://some-download-path");
}

同时监听多个令牌

可以使用CancellationTokenSource.CreateLinkedTokenSource方法将多个令牌链接到一个令牌中。

推荐的设计模式

  1. 知道何时已经过了不可取消的点
  2. 传播CancellationToken
  3. 完成工作后不要抛出OperationCancelledException
  4. 输入验证
  5. 考虑根本不检查令牌

取消令牌的可选参数

如果要接受CancellationToken但希望将其设为可选,可以使用如下语法:

public Task SomethingExpensiveAsync(CancellationToken cancellationToken = default(CancellationToken))
{
  // don't worry about NullReferenceException if the
  // caller omitted the argument because it's a struct.
  cancellationToken.ThrowIfCancellationRequested();
}

如何处理取消异常

如果遇到取消,可能会遇到以下几种类型的异常:TaskCanceledExceptionOperationCanceledException。在编写处理已取消操作的后果的捕获块时,应捕获OperationCanceledException

async Task SendResultAsync(CancellationToken cancellationToken)
{
  try
  {
    await httpClient.SendAsync(form, cancellationToken);
  }
  catch (OperationCanceledException ex)
  {
    // perform your cleanup
    form.Dispose();
    // rethrow exception so caller knows you've canceled.
    // DON'T "throw ex;" because that stomps on
    // the Exception.StackTrace property.
    throw;
  }
}

参考资料

  1. Microsoft文档:取消托管线程
  2. 推荐模式:CancellationToken的最佳实践
  3. Andrew Lock的博客:在MVC控制器中使用CancellationToken
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号