需要在异步操作中提供终止功能时,最常用的方法是使用 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); // 代替耗时的操作}}
}