CancellationTokenSource,如何巧妙利用CancellationTokenSource来优雅地取消异步操作
`CancellationTokenSource` 是 .NET 框架中用于创建、管理和取消 `CancellationToken` 的类。`CancellationToken` 本身是一个值类型,它表示一个取消操作的请求,而 `CancellationTokenSource` 是这个请求的源头。在异步操作中,使用 `CancellationTokenSource` 可以优雅地取消正在进行的操作,而不需要中断或中止整个方法或线程。
下面将详细解释如何巧妙利用 `CancellationTokenSource` 来优雅地取消异步操作:
1. 创建 `CancellationTokenSource`
你需要创建一个 `CancellationTokenSource` 实例。这个实例将用于生成 `CancellationToken`。
csharp
using System.Threading;
// 创建一个 CancellationTokenSource
var cts = new CancellationTokenSource();
// 获取 CancellationToken
var token = cts.Token;
2. 将 `CancellationToken` 传递给异步操作
将 `CancellationToken` 传递给启动的异步操作。这通常通过 `async` 方法中的参数来实现。
csharp
public async Task DoWorkAsync(CancellationToken token)
{
try
{
// 模拟长时间运行的任务
for (int i = 0; i < 10; i++)
{
await Task.Delay(1000, token);
// 检查是否收到取消请求
if (token.IsCancellationRequested)
{
// 抛出 OperationCanceledException,表示操作被取消
throw new TaskCanceledException("Operation canceled by user.");
}
Console.WriteLine($"Working... {i}");
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was canceled.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
3. 使用 `CancellationTokenSource` 取消操作
当需要取消异步操作时,调用 `CancellationTokenSource` 的 `Cancel` 方法。
csharp
// 调用异步方法,传入 CancellationToken
var task = DoWorkAsync(token);
// 在需要取消的时候调用 Cancel 方法
// 例如,在按钮点击事件中
// cts.Cancel();
4. 在异步操作中处理取消请求
在异步操作内部,定期检查 `CancellationToken` 的 `IsCancellationRequested` 属性,并据此决定是否继续执行。
csharp
if (token.IsCancellationRequested)
{
// 抛出 OperationCanceledException,表示操作被取消
throw new TaskCanceledException("Operation canceled by user.");
}
5. 捕获 `OperationCanceledException`
在调用异步方法的代码中,使用 `try/catch` 块来捕获 `OperationCanceledException`,并据此处理取消操作。
csharp
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was canceled.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
6. 释放 `CancellationTokenSource` 资源
当你不再需要 `CancellationTokenSource` 时,调用其 `Dispose` 方法来释放资源。
csharp
// 释放资源
cts.Dispose();
示例
下面是一个完整的示例,展示了如何使用 `CancellationTokenSource` 来取消异步操作:
csharp
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// 创建一个 CancellationTokenSource
var cts = new CancellationTokenSource();
// 获取 CancellationToken
var token = cts.Token;
// 调用异步方法,传入 CancellationToken
var task = DoWorkAsync(token);
// 在 Main 方法中等待一段时间,然后取消操作
await Task.Delay(5000);
// 取消操作
cts.Cancel();
// 等待异步操作完成
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was canceled.");

}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
// 释放资源
cts.Dispose();
}
static async Task DoWorkAsync(CancellationToken token)
{
try
{
// 模拟长时间运行的任务
for (int i = 0; i < 10; i++)
{
await Task.Delay(1000, token);
// 检查是否收到取消请求
if (token.IsCancellationRequested)
{
// 抛出 OperationCanceledException,表示操作被取消
throw new TaskCanceledException("Operation canceled by user.");
}
Console.WriteLine($"Working... {i}");
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was canceled.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}
在这个示例中,`DoWorkAsync` 方法模拟了一个长时间运行的异步操作,通过 `CancellationToken` 来检查是否收到取消请求。在 `Main` 方法中,我们等待一段时间然后取消操作,并捕获 `OperationCanceledException` 来处理取消操作。
注意事项
- 当你调用 `CancellationTokenSource.Cancel` 方法时,它不会立即停止正在进行的操作,而是向操作发送一个取消请求。操作应该定期检查这个请求,并据此决定是否继续执行。
- 如果操作在收到取消请求后选择抛出 `OperationCanceledException`,那么调用方可以通过捕获这个异常来优雅地处理取消操作。
- 当你不再需要 `CancellationTokenSource` 时,应该调用其 `Dispose` 方法来释放资源。
- 在使用 `CancellationTokenSource` 时,请确保在适当的时候取消操作,以避免资源泄漏或长时间运行不必要的操作。
- 当你调用 `CancellationTokenSource.Cancel` 方法后,`IsCancellationRequested` 属性将返回 `true`,表示已经发出取消请求。
- `CancellationTokenSource` 是线程安全的,可以在多个线程上安全地访问和取消操作。
通过巧妙地使用 `CancellationTokenSource`,你可以优雅地取消异步操作,而不需要中断整个方法或线程。这有助于提高代码的可维护性和健壮性。

