我创建一个像这样的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
,实现 将触发传递给 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 MyServiceController: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 MyServiceController:Controller
{
ICustomServiceStopper _stopper;
public MyServiceController(ICustomServiceStopper stopper)
{
_stopper=stopper;
}
[HttpPost]
public async Task<IActionResult> Stop()
{
await _stopper.StopAsync();
return Ok();
}
}
已经晚了,但只需触发 StopAsync(CancellationToken CancellationToken) 方法,该方法是 BackgroundService 接口的一部分,它只会停止你当前的工作进程,而不是整个应用程序:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var stopCounter = 10;
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
stopCounter--;
if (stopCounter < 1)
{
StopAsync(stoppingToken);
}
}
}
看来我之前的答案是错误的。
ExecuteAsync
参数是StartAsync
方法提供的令牌。
BackgroundService
代币源由StopAsync
方法取消。
因此,要取消
CustomService
异步工作,您必须调用 StopAsync
方法。此取消令牌作为参数提供给 ExecuteAsync
方法。