问题描述
我像这样创建了一个 BackgroundService:
public class CustomService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
do
{
//...
await Task.Delay(60000,cancellationToken);
}
while (!cancellationToken.IsCancellationRequested);
}
}
如何手动取消?
解决方法
目前还不清楚您是要取消所有服务,也许是应用程序本身(或至少是主机),或者只是一项服务。
停止应用
要取消应用程序,请在类中注入 IHostApplicationLifetime 接口,该接口将强制取消并在需要时调用 StopApplication。如果你想从后台服务本身内部取消,可能是因为没有别的事情可做,那就是你需要注入的地方。
StopApplication
会告诉主机应用程序需要关闭。主机将对所有托管服务调用 StopAsync
。由于您使用 BackgroundService
,the implementation 将触发传递给 cancellationToken
的 ExecuteAsync
:
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executeTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executeTask,Task.Delay(Timeout.Infinite,cancellationToken)).ConfigureAwait(false);
}
}
您根本不必更改当前代码。唯一需要担心的是 await Task.Delay()
会泄漏计时器。最好显式使用 Timer
,并在触发取消时处理它。
例如,如果您想从控制器操作中关闭应用程序:
public class MyServiceControllerr:Controller
{
IHostApplicationLifetime _lifetime;
public MyServiceController(IHostApplicationLifetime lifeTime)
{
_lifeTime=lifeTime;
}
[HttpPost]
public IActionResult Stop()
{
_lifeTime.StopApplication();
return Ok();
}
}
停止服务
如果您只想停止这一项服务,您需要一种从其他代码调用其 StopAsync
方法的方法。有很多方法可以做到这一点。一种这样的方法是将 CustomService
注入调用者并调用 StopAsync
。不过,这不是一个好主意,因为它公开了服务并将控制器/停止代码与服务耦合。对此进行测试也不容易。
另一种可能是为调用 StopAsync
创建一个接口,例如:
public interface ICustomServiceStopper
{
Task StopAsync(CancellationToken token=default);
}
public class CustomService : BackgroundService,ICustomServiceStopper
{
...
Task ICustomServiceStopper.StopAsync(CancellationToken token=default)=>base.StopAsync(token);
}
将接口注册为单例:
services.AddSingleton<ICustomServiceStopper,CustomService>();
并在需要时注入 ICustomServiceStopper
:
public class MyServiceControllerr:Controller
{
ICustomServiceStopper _stopper;
public MyServiceController(ICustomServiceStopper stopper)
{
_stopper=stopper;
}
[HttpPost]
public async Task<IActionResult> Stop()
{
await _stopper.StopAsync();
return Ok();
}
}
,
我已经检查了 BackgroundService source code
看来我之前的回答是错误的。
ExecuteAsync
参数是 StartAsync
方法提供的令牌。
BackgroundService
令牌源被 StopAsync
方法取消。
因此要取消 CustomService
异步工作,您必须调用 StopAsync
方法。此取消标记作为参数提供给 ExecuteAsync
方法。