在 .Net Core 中使用 CancellationToken 的最佳做法

科技   2024-12-14 06:21   北京  


在本文中,我们将了解CancellationToken(取消令牌)以及如何使用它。

在过去,当我们执行一些耗时的操作,比如耗时的数据库调用、耗时的HTTP调用时,我们没办法取消这些操作。

在应用程序中使用CancellationToken有诸多好处,尤其在提高响应能力、资源管理以及用户体验方面。以下是一些关键优势:

1. 提高响应能力

  • 用户体验:能够快速取消长时间运行任务的应用程序可以对用户操作做出快速响应,例如取消文件上传或下载,从而带来更好的用户体验。

  • 实时调整:用户可以调整自己的操作(比如停止搜索操作),而无需等待当前操作完成。

2. 资源管理

  • 高效利用资源:取消不必要的操作可以防止CPU、内存以及其他资源的浪费。这对于服务器端应用程序尤为重要,因为在这类应用中资源是由众多用户共享的。

  • 数据库连接:在数据库操作中,使用CancellationToken可以释放那些原本会被长时间运行的查询占用的数据库连接。

3. 可扩展性

  • 处理高负载:应用程序可以通过取消不再需要的操作更从容地应对高负载情况,从而为新传入的请求释放资源。

  • 并发控制:有效的取消操作有助于管理并发任务,确保资源分配给最关键的操作。

4. 错误处理与稳定性

  • 优雅关闭:当应用程序需要关闭或重启时,CancellationToken允许正在进行的操作优雅地终止,降低数据损坏或状态不一致的风险。

  • 防止死锁:通过取消耗时过长的操作,可以降低出现死锁及其他并发问题的可能性。

5. 超时实现

自动超时:使用CancellationTokenSource.CancelAfter来实现超时,可以自动取消超出指定时长的任务,确保应用程序保持响应能力。

6. 任务协调

  • 协作式取消CancellationToken允许进行协作式取消,操作会定期检查令牌,看是否有取消请求,以便操作能够进行清理并优雅地终止。

  • 关联取消令牌:可以将多个CancellationTokenSource实例关联在一起,以便同时取消多个操作,这简化了复杂任务的协调工作。

7. 简化代码维护

  • 标准模式:使用CancellationToken建立了一种用于任务取消的标准模式,使代码更易于理解和维护。

  • 统一处理:它提供了一种在应用程序的不同部分(从用户发起的取消操作到自动超时以及应用程序关闭等情况)统一处理取消操作的方式。

示例用例

  • Web应用程序:取消不再需要的数据库查询、文件上传/下载以及API调用。

  • 桌面应用程序:允许用户取消长时间运行的操作,如图像处理或数据分析。

  • 移动应用程序:通过在不再需要后台操作时取消它们来延长电池续航时间。

  • 云服务:通过响应伸缩事件或维护时段来取消操作,从而高效地管理资源。

以下是一个展示如何使用CancellationToken取消正在进行的操作的简化示例:

public async Task LongRunningOperationAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 100; i++)
{
// 定期检查是否有取消请求
cancellationToken.ThrowIfCancellationRequested();

// 模拟一些工作
await Task.Delay(100);
}
}

public async Task MainAsync()
{
using var cts = new CancellationTokenSource();

// 在2秒后取消操作
cts.CancelAfter(TimeSpan.FromSeconds(2));

try
{
await LongRunningOperationAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("操作已被取消。");
}
}

运行代码时:

当我们运行这段代码时,会发现出现了OperationCanceledException(操作已取消异常)。 

使用CancellationToken在响应能力、资源管理以及可扩展性方面有着显著的益处。通过在应用程序中合理地运用它,你可以极大地提升整体性能和用户体验。

取消HTTP调用

我们可以使用CancellationToken取消耗时的HttpClient调用。这样一来,如果请求耗时过长或者满足了其他某些条件,你就可以中止该请求。以下是具体的操作方法:

1. 使用CancellationTokenSource

你可以创建一个CancellationTokenSource,并将它的令牌传递给HttpClient调用。然后你可以在特定的超时时间后或者基于某些条件来取消该令牌。

看看下面的代码:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static async Task Main(string[] args)
{
using var httpClient = new HttpClient();
using var cts = new CancellationTokenSource();

// 为请求设置超时时间
cts.CancelAfter(TimeSpan.FromSeconds(10)); // 10秒后取消

try
{
var response = await httpClient.GetAsync("https://example.com", cts.Token);
response.EnsureSuccessStatusCode();

var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (OperationCanceledException)
{
Console.WriteLine("请求已被取消。");
}
catch (HttpRequestException e)
{
Console.WriteLine($"请求出错:{e.Message}");
}
}
}

2. 手动取消请求

你可以通过调用CancellationTokenSourceCancel方法来手动取消请求:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static async Task Main(string[] args)
{
using var httpClient = new HttpClient();
using var cts = new CancellationTokenSource();

var task = httpClient.GetAsync("https://example.com", cts.Token);

// 模拟一些用于取消请求的条件
Task.Delay(2000).ContinueWith(_ => cts.Cancel());

try
{
var response = await task;
response.EnsureSuccessStatusCode();

var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (OperationCanceledException)
{
Console.WriteLine("请求已被取消。");
}
catch (HttpRequestException e)
{
Console.WriteLine($"请求出错:{e.Message}");
}
}
}

解释:

  • 创建CancellationTokenSource:创建CancellationTokenSource的一个实例,它会提供一个CancellationToken

  • 设置超时时间:使用cts.CancelAfter(TimeSpan.FromSeconds(x))在指定时长后自动取消请求。

  • 将令牌传递给HttpClient调用:将CancellationToken传递给GetAsync方法。

  • 处理异常:捕获OperationCanceledException来处理取消的情况。

通过使用CancellationToken,你可以在.NET Core中有效地管理并取消长时间运行的HttpClient调用。

如果你喜欢我的文章,请给我一个赞!谢谢

架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章