了解BackgroundService和CancellationToken

Ung*_*ant 3 c# service background-service cancellationtokensource cancellation-token

我对此很陌生。周围的信息很多,我有点困惑。我想确认我的理解是否正确并感谢任何帮助。我将在 (i) 中对每个问题进行编号,以便更容易回答。

我有一个带有 ExecuteAsync(CancellationToken CancellationToken) 和 StopAsync(CancellationToken CancellationToken) 的BackgroundService。

我假设这两种方法在服务运行时都会获得相同的令牌。(1) 这是真的吗?

如果我理解正确,只要取消标记未标记为取消,我就应该使用 ExecuteAsync 中的取消标记来执行某些操作。ExecuteAsync 有一个 while 循环,在取消令牌未取消时运行迭代。每次迭代都会等待某些事情发生,并可能启动一些异步任务。

当我启动这些任务时,我应该从链接到cancellationToken的新CancellationTokenSource给它们一个CancellationToken,以便(A)任务知道何时应该取消服务以及何时应该取消任务本身(但是,在后一种情况,无需取消服务本身);(B) 使cancellationToken用完。(2) (A)和(B)正确吗?

简单地在某些 CancellationTokenSource 上调用 Cancel() 实际上并不会自动取消我的任务或引发异常。这个逻辑由我来定义。(3)这是真的吗?

这让我想到了另一个问题。假设我发现内部某个任务确实被标记为取消。什么是正确/预期的行为?我应该清理任务的资源,然后对我拥有的令牌使用 ThrowIfCancellationRequested() 吗?(4)这是真的吗?

最后,当服务本身被取消时,即当 ExecuteAsync 的主循环发现其令牌被取消时,我应该如何表现。(5) 我应该立即 ThrowIfCancellationRequested 吗?我应该先清理资源,然后再 ThrowIfCancellationRequested 吗?或者我应该只调用 ThrowIfCancellationRequested 作为 StopAsync 的最后一行?

Ste*_*ary 5

我有一个带有 ExecuteAsync(CancellationToken CancellationToken) 和 StopAsync(CancellationToken CancellationToken) 的BackgroundService。

不。您在这里混合了不同级别的抽象。

BackgroundService要么与 一起使用ExecuteAsync ,要么IHostedServiceStartAsyncand一起使用StopAsync

你可能只需要ExecuteAsync. 这对大多数人来说都很好。

我假设这两种方法在服务运行时都会获得相同的令牌。(1) 这是真的吗?

StartAsync不会。传递给、StopAsync和的取消标记ExecuteAsync都是不同的。

如果我理解正确,只要取消标记未标记为取消,我就应该使用 ExecuteAsync 中的取消标记来执行某些操作。

是的。ExecuteAsync当后台服务需要停止时,传递给的取消令牌将被取消。

当我启动这些任务时,我应该从链接到cancellationToken的新CancellationTokenSource给它们一个CancellationToken

您是否只需要取消一个任务?如果是这样,那么是的。如果没有的话,直接传过去就可以了cancellationToken

简单地在某些 CancellationTokenSource 上调用 Cancel() 实际上并不会自动取消我的任务或引发异常。这个逻辑由我来定义。(3)这是真的吗?

是的。必须遵守取消标记。这通常是通过以下方式完成的:

  • 将它们传递给任何接受它们的方法。99% 的情况下,这就是您需要做的全部事情。
  • (主要针对 CPU 密集型任务)定期调用ThrowIfCancellationRequested.
  • (主要针对 I/O 密集型任务)注册回调来执行实际的取消。

这让我想到了另一个问题。假设我发现内部某个任务确实被标记为取消。什么是正确/预期的行为?我应该清理任务的资源,然后对我拥有的令牌使用 ThrowIfCancellationRequested() 吗?(4)这是真的吗?

更好:始终在报表中定义您的资源using。然后您就可以ThrowIfCancellationRequested在需要时进行操作。

同样,99% 的情况下,正确处理取消只需将令牌传递给其他已经支持取消的方法即可。

最后,当服务本身被取消时,即当 ExecuteAsync 的主循环发现其令牌被取消时,我应该如何表现。(5) 我应该立即 ThrowIfCancellationRequested 吗?我应该先清理资源,然后再 ThrowIfCancellationRequested 吗?

以同样的方式处理它:如果您的方法是可取消的,只需将其向下传递即可。将资源保存在using块中。如果您需要检查(大多数不需要),您可以定期致电ThrowIfCancellationRequested.

具体来说:

每次迭代都会等待某些事情发生,并可能启动一些异步任务。

如果您的“等待某事发生”是可取消的,并且您的“启动一些异步任务”是可取消的,那么只需将取消令牌传递给这两个方法即可。无需进行轮询。

或者我应该只调用 ThrowIfCancellationRequested 作为 StopAsync 的最后一行?

StopAsync如果您正在使用,请不要使用ExecuteAsync