1. 深入理解.NET中的CancellationTokenSource
在.NET开发中,异步编程已经成为现代应用程序的基础构建块。随着应用程序变得越来越复杂,能够优雅地取消长时间运行的操作变得至关重要。CancellationTokenSource就是.NET为我们提供的强大工具,它允许我们以协作方式取消异步操作。
CancellationTokenSource的核心思想是"协作式取消"——它不会强制终止线程或操作,而是提供一种机制,让操作可以定期检查是否收到取消请求,并据此决定是否提前结束。这种方式比强制终止线程更安全,避免了资源泄漏和状态不一致的问题。
重要提示:CancellationTokenSource实现了IDisposable接口,使用后应及时释放以避免内存泄漏。最佳实践是在using语句中使用它。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. CancellationTokenSource的基本使用
2.1 创建与取消操作
创建一个CancellationTokenSource实例非常简单:
csharp复制var cts = new CancellationTokenSource();
获取关联的CancellationToken:
csharp复制CancellationToken token = cts.Token;
要取消操作,只需调用Cancel方法:
csharp复制cts.Cancel();
2.2 传递给异步方法
典型的异步方法使用模式如下:
csharp复制public async Task DoWorkAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
// 执行工作
await Task.Delay(1000, cancellationToken);
}
cancellationToken.ThrowIfCancellationRequested();
}
2.3 组合使用示例
下面是一个完整的示例,展示如何创建、传递和处理取消请求:
csharp复制async Task Main()
{
using var cts = new CancellationTokenSource();
var token = cts.Token;
// 启动异步任务
var task = LongRunningOperationAsync(token);
// 模拟用户取消
await Task.Delay(2500);
cts.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("操作已取消");
}
}
async Task LongRunningOperationAsync(CancellationToken token)
{
try
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
Console.WriteLine($"工作进度: {i * 10}%");
await Task.Delay(500, token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("清理资源...");
throw;
}
}
3. 高级功能与技巧
3.1 超时取消
CancellationTokenSource内置了对超时的支持:
csharp复制// 5秒后自动取消
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
3.2 链接多个CancellationToken
有时我们需要同时监听多个取消源,可以使用CreateLinkedTokenSource:
csharp复制var cts1 = new CancellationTokenSource();
var cts2 = new CancellationTokenSource();
// 当任一取消源请求取消时,linkedToken会触发
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);
3.3 取消回调注册
可以注册在取消时执行的回调:
csharp复制cts.Token.Register(() =>
{
Console.WriteLine("取消请求已收到");
// 执行清理工作
});
4. 实际应用场景
4.1 Web请求取消
在ASP.NET Core中,控制器方法会自动获得一个CancellationToken参数,当客户端断开连接时会触发取消:
csharp复制[HttpGet]
public async Task<IActionResult> GetData(CancellationToken cancellationToken)
{
var data = await _service.FetchDataAsync(cancellationToken);
return Ok(data);
}
4.2 UI应用程序中的后台任务
在WPF或WinForms中,当用户关闭窗口时可以取消后台任务:
csharp复制private CancellationTokenSource _cts;
private async void StartButton_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
try
{
await ProcessDataAsync(_cts.Token);
}
catch (OperationCanceledException)
{
MessageBox.Show("操作已取消");
}
}
private void CancelButton_Click(object sender, EventArgs e)
{
_cts?.Cancel();
}
4.3 并行处理
在并行处理中取消所有任务:
csharp复制async Task ProcessAllItemsAsync(List<Item> items, CancellationToken token)
{
var tasks = items.Select(item => ProcessItemAsync(item, token));
await Task.WhenAll(tasks);
}
async Task ProcessItemAsync(Item item, CancellationToken token)
{
// 处理单个项目,定期检查取消请求
}
5. 最佳实践与常见问题
5.1 资源清理
确保在取消操作时正确清理资源:
csharp复制async Task OperationWithResourceAsync(CancellationToken token)
{
var resource = AcquireResource();
try
{
await DoWorkAsync(resource, token);
}
finally
{
resource.Dispose();
}
}
5.2 取消响应速度
对于长时间运行的操作,应定期检查取消请求,但检查频率不宜过高,以免影响性能。通常在每个逻辑工作单元完成后检查一次。
5.3 常见错误
-
忘记传递CancellationToken:确保将token传递给所有支持取消的异步方法。
-
忽略OperationCanceledException:应适当处理取消异常,通常不需要记录为错误。
-
过度使用全局CancellationTokenSource:应尽可能限制CancellationTokenSource的作用域。
6. 性能考量
6.1 内存开销
每个CancellationTokenSource实例都会分
