需要在异步操作中提供终止功能时,最常用的方法是使用 CancellationToken
。这个工具可以让你在需要时优雅地取消异步操作,而不是强制终止线程。
.NET 中不建议直接终止线程,因为这可能导致资源泄露和数据不一致等问题。强制终止线程可能会中断一个正在执行的操作,甚至可能使应用程序处于未定义的状态。
1. 什么是 CancellationToken
?
CancellationToken
是 .NET 提供的一个结构,用于处理异步任务的取消。它允许你在长时间运行的操作中检查用户是否请求取消,从而保证程序的响应性。
2. 使用 CancellationToken
的基本步骤
以下是使用 CancellationToken
的关键步骤,这些步骤在示例代码中都有体现:
步骤 1:创建 CancellationTokenSource
首先,创建一个 CancellationTokenSource
实例。这个实例提供了一个 CancellationToken
,可以将其传递给异步方法。
static CancellationTokenSource cts = new CancellationTokenSource();
步骤 2:获取 CancellationToken
通过 CancellationTokenSource
获取一个 CancellationToken
,这个令牌将用于检查取消请求。
var token = cts.Token;
步骤 3:传递 CancellationToken
给异步方法
在定义的异步方法中,接受 CancellationToken
参数,并在适当的位置检查它的状态。
static async Task LongRunningOperation(CancellationToken token)
{
for (int i = 0; i < 100; i++)
{
token.ThrowIfCancellationRequested(); // 检查是否请求取消
// 模拟耗时操作
Console.WriteLine($"处理第 {i + 1} 项...");
await Task.Delay(100); // 代替耗时的操作
}
}
在循环中,我们调用 token.ThrowIfCancellationRequested()
方法。如果调用此方法时取消请求已经发出,则会抛出 OperationCanceledException
,从而允许我们在主方法中捕获并处理。
步骤 4:请求取消
在用户按下某个键(例如 'c' 键)时,你可以请求取消操作。这通过调用 CancellationTokenSource.Cancel
方法来实现。
if (Console.ReadKey().KeyChar == 'c')
{
cts.Cancel(); // 请求取消
}
步骤 5:处理取消
在主方法中,通过 try-catch
块捕获 OperationCanceledException
,以便在操作被取消时进行适当处理。
try
{
await processingTask; // 等待任务完成
}
catch (OperationCanceledException)
{
Console.WriteLine("操作已被取消。");
}
完整代码
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static CancellationTokenSource cts = new CancellationTokenSource();
static async Task Main(string[] args)
{
Console.WriteLine("按下任意键开始长时间操作...");
Console.ReadKey();
var processingTask = LongRunningOperation(cts.Token);
Console.WriteLine("按下 'c' 键取消操作...");
if (Console.ReadKey().KeyChar == 'c')
{
cts.Cancel(); // 请求取消
}
try
{
await processingTask; // 等待任务完成
Console.WriteLine("操作完成。");
}
catch (OperationCanceledException)
{
Console.WriteLine("操作已被取消。");
}
finally
{
cts.Dispose();
}
}
static async Task LongRunningOperation(CancellationToken token)
{
for (int i = 0; i < 100; i++)
{
token.ThrowIfCancellationRequested(); // 检查是否请求取消
// 模拟耗时操作
Console.WriteLine($"处理第 {i + 1} 项...");
await Task.Delay(100); // 代替耗时的操作
}
}
}