在C#的异步编程中,异步任务一旦启动就会独立执行,很多时候我们需要根据业务需求中途停止这些任务,比如用户点击取消按钮、请求超时等场景,这时候就需要用到CancellationToken来实现任务取消逻辑。

CancellationToken核心概念
CancellationToken是.NET提供的用于传播取消操作的轻量级结构,它需要和CancellationTokenSource配合使用。CancellationTokenSource负责发起取消请求,而CancellationToken则负责将取消请求传递给异步任务,任务内部通过检查令牌状态来决定是否终止执行。
核心类型说明
- CancellationTokenSource:取消令牌的创建者,调用其Cancel方法可以触发取消请求,也可以设置超时自动取消。
- CancellationToken:取消信号的载体,异步任务通过它感知取消请求,支持轮询检查状态或者注册取消回调。
基础使用步骤
使用CancellationToken取消异步任务的整体流程可以分为三步:创建取消令牌源、将令牌传递给异步任务、在任务内部处理取消逻辑。
第一步:创建CancellationTokenSource
首先我们需要实例化一个CancellationTokenSource对象,它会生成一个关联的CancellationToken,后续把这个令牌传递给需要支持取消的异步方法。
using System;
using System.Threading;
class Program
{
static void Main()
{
// 创建取消令牌源
CancellationTokenSource cts = new CancellationTokenSource();
// 获取关联的取消令牌
CancellationToken token = cts.Token;
}
}
第二步:传递令牌到异步方法
定义异步方法时,需要添加一个CancellationToken类型的参数,调用方法时把上一步获取的令牌传入。如果方法是.NET内置的支持取消的API,比如HttpClient的GetAsync、Task.Delay等,直接传入令牌即可。
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 调用支持取消的异步方法,传入令牌
Task workTask = DoWorkAsync(token);
// 模拟3秒后用户触发取消
await Task.Delay(3000);
cts.Cancel();
try
{
await workTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("任务已被取消");
}
}
// 自定义支持取消的异步方法
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
// 检查是否收到取消请求
token.ThrowIfCancellationRequested();
Console.WriteLine($"正在执行第{i + 1}次操作");
await Task.Delay(1000);
}
}
}
第三步:在任务内部处理取消
异步方法内部有两种常见的处理取消的方式:一种是调用token.ThrowIfCancellationRequested()方法,如果收到取消请求就直接抛出OperationCanceledException异常,终止任务执行;另一种是轮询检查token.IsCancellationRequested属性,手动处理取消逻辑后再退出。
static async Task DoWorkWithManualCheckAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
// 手动检查取消状态
if (token.IsCancellationRequested)
{
Console.WriteLine("收到取消请求,正在清理资源后退出");
// 这里可以添加资源释放等逻辑
return;
}
Console.WriteLine($"手动检查模式:正在执行第{i + 1}次操作");
await Task.Delay(1000);
}
}
常见使用场景
超时自动取消
很多时候我们需要任务执行超过一定时间就自动取消,这时候可以在创建CancellationTokenSource时指定超时时间,不需要手动调用Cancel方法。
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// 创建5秒后自动取消的令牌源
using CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
await LongRunWorkAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("任务执行超时,已自动取消");
}
}
static async Task LongRunWorkAsync(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("长时间运行的任务正在执行");
await Task.Delay(1000);
}
}
}
注册取消回调
如果需要在取消发生时执行一些额外的逻辑,比如释放资源、记录日志,可以通过token.Register方法注册取消回调,回调会在取消请求触发时执行。
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 注册取消回调
token.Register(() =>
{
Console.WriteLine("取消回调触发:正在释放相关资源");
});
Task workTask = DoWorkAsync(token);
await Task.Delay(2000);
cts.Cancel();
try
{
await workTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("任务已取消");
}
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine($"执行第{i + 1}次操作");
await Task.Delay(1000);
}
}
}
注意事项
- CancellationTokenSource实现了IDisposable接口,使用完成后需要及时调用Dispose方法释放资源,或者使用using语句自动释放。
- 不要在异步方法中忽略取消令牌,即使当前不需要取消逻辑,也建议把令牌作为参数传递,方便后续扩展。
- 如果多个异步任务需要共享同一个取消信号,可以把同一个CancellationToken传递给多个任务,触发取消时所有任务都会收到信号。
- 抛出OperationCanceledException时,如果希望上层能区分是主动取消还是其他异常,可以传入对应的CancellationToken到异常构造函数中。
常见问题解答
为什么调用Cancel后任务没有立即停止?
CancellationToken的取消是协作式的,也就是说任务需要主动检查取消状态才会响应,并不是调用Cancel后任务会立刻被强制终止。如果任务内部没有检查令牌状态的逻辑,即使调用了Cancel,任务也会继续执行直到完成。
取消令牌可以重复使用吗?
不可以,一旦CancellationTokenSource触发了取消,它的状态就会变为已取消,无法重置。如果需要再次发起取消,需要创建新的CancellationTokenSource实例。
正确使用CancellationToken可以让异步任务的取消逻辑更加清晰可控,避免强制终止任务带来的资源泄漏问题,是C#异步编程中非常重要的一个知识点。
CancellationTokenC#异步编程异步任务取消Task取消令牌修改时间:2026-07-12 14:45:32