CancellationTokenSource
是 .NET 中用于实现任务或异步操作的 取消机制 的一个核心类。通过它,我们可以向异步任务发送取消信号,从而安全地终止任务的执行,避免不必要的资源消耗。
核心概念
-
CancellationTokenSource
:- 用于发出取消信号的类。
- 可以通过它生成一个
CancellationToken
,将其传递给任务或异步方法。 - 当调用
Cancel
方法时,表示发出了取消信号,任务会根据此信号选择是否终止。
-
CancellationToken
:- 由
CancellationTokenSource
提供,用于监听取消信号。 - 异步任务可以通过检查
IsCancellationRequested
属性或调用ThrowIfCancellationRequested
方法来检测取消信号。
- 由
基本用法
示例代码:使用 CancellationTokenSource
取消任务
using System;
using System.Threading;
using System.Threading.Tasks;class Program
{static async Task Main(string[] args){// 创建 CancellationTokenSourceCancellationTokenSource cts = new CancellationTokenSource();// 获取 CancellationTokenCancellationToken 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); // 模拟工作}}
}
输出示例
-
如果未取消任务:
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); // 模拟工作}}
}
输出示例
-
如果未取消:
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); // 模拟工作}}
}
输出示例
-
如果未取消
Working... 0
Working... 1
Working... 2
...
2.如果取消:
Working... 0
Working... 1
Task was cancelled.
注意事项
-
取消是协作的:
CancellationTokenSource
只是发出取消信号,任务本身需要主动响应取消(例如通过检查IsCancellationRequested
或调用ThrowIfCancellationRequested
)。- 如果任务忽略了取消信号,它仍然会继续运行。
-
Dispose
的使用:- 使用完
CancellationTokenSource
后,应该调用Dispose
方法以释放资源,特别是在频繁创建和销毁时。
- 使用完
-
异常处理:
- 当
ThrowIfCancellationRequested
被调用时,会抛出OperationCanceledException
。这种异常通常用于标识任务的正常取消,而不是错误。
- 当
-
线程安全:
CancellationToken
和CancellationTokenSource
是线程安全的,可以在多个线程之间安全地共享。
总结
CancellationTokenSource
和CancellationToken
提供了优雅的任务取消机制。- 它们适用于长时间运行的任务、异步操作或需要动态中断的场景。
- 通过合理使用
CancellationTokenSource
和CancellationToken
,可以让你的程序更加高效,避免无谓的资源浪费。