c# - 我应该何时调用 CancellationToken.ThrowIfCancellationRequested?
问题描述
我开发了一个基于 C# 的 Windows 服务,它在几个不同的任务中运行它的所有逻辑。为了让服务在停止时正常关闭,我使用了一个 CancellationToken ,它被传递给任何接受一个(主要来自我正在使用的第 3 方库)的函数,以便在完成之前中止处理。
我注意到,在调用函数OperationCanceledException
时请求取消时,这些函数都没有抛出异常,所以我的应用程序只是继续执行,直到我ThrowIfCancellationRequested()
稍后在代码中调用其他地方。我是否应该ThrowIfCancellationRequested()
在调用每个函数后手动调用以确保任务尽快停止,或者我应该什么时候调用ThrowIfCancellationRequested()
自己的代码?
解决方案
是的,您应该ThrowIfCancellationRequested()
在代码中的适当位置手动调用(适当的位置由您作为程序员确定)。
考虑以下简单作业处理函数的示例,该函数从队列中读取作业并对其进行处理。这些评论说明了开发人员在决定是否检查取消时可能会经历的那种思考。
另请注意,您是对的 - 接受令牌的标准框架函数不会引发取消异常 - 它们只会提前返回,因此您必须自己检查取消。
public async Task DoWork(CancellationToken token)
{
while(true)
{
// It is safe to check the token here, as we have not started any work
token.ThrowIfCancellationRequested();
var nextJob = GetNextJob();
// We can check the token here, because we have not
// made any changes to the system.
token.ThrowIfCancellationRequested();
var jobInfo = httpClient.Get($"job/info/{nextJob.Id}", token);
// We can check the token here, because we have not
// made any changes to the system.
// Note that HttpClient won't throw an exception
// if the token is cancelled - it will just return early,
// so we must check for cancellation ourselves.
token.ThrowIfCancellationRequested();
// The following code is a critical section - we are going to start
// modifying various databases and things, so don't check for
// cancellation until we have done it all.
ModifySystem1(nextJob);
ModifySystem2(nextJob);
ModifySystem3(nextJob);
// We *could* check for cancellation here as it is safe, but since
// we have already done all the required work *and* marking a job
// as complete is very fast, there is not a lot of point.
MarkJobAsCompleted(nextJob);
}
}
最后,您可能不想从您的代码中泄露取消异常,因为它们不是“真正的”异常——只要有人停止您的服务,它们就会发生。
您可以使用异常过滤器捕获异常,如下所示:
public async Task DoWork(CancellationToken token)
{
try
{
while(true)
{
// Do job processing
}
}
catch (OperationCanceledException e) when (e.CancellationToken == token)
{
Log.Info("Operation cancelled because service is shutting down.");
}
catch (Exception e)
{
Log.Error(e, "Ok - this is actually a real exception. Oh dear.");
}
}
推荐阅读
- flutter - generated_plugin_registrant.dart 中的导入不完整
- flutter - 更新时 Flutter SqfliteDatabaseException
- reactjs - 无法与 firebase 实时数据库模拟器交互
- html - 如何防止内容显示在 div 面板下
- python - Pandas:根据另一列中存在的组对一列中的所有值进行归一化,介于 0 和 10 之间
- javascript - 曾经工作的 Google App 脚本现在在 UrlfetchApp 上获得“访问权限未授予或过期”。在 Chrome 浏览器中工作的获取获取
- reactjs - id 为“sc-iseJRi”的组件 styled.p 已动态创建
- azure - 了解 Azure B2C 中即时迁移自定义策略的响应
- android - 我们如何使用 Google Pay 测试错误和失败案例?
- c - 如何在 C 中将 dns 名称转换为主机名字符串?