如何在ASP.net core中手动取消BackgroundService

问题描述 投票:0回答:3

我创建一个像这样的BackgroundService:

public class CustomService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        do
        {
            //...

            await Task.Delay(60000, cancellationToken);
        }
        while (!cancellationToken.IsCancellationRequested);
    }
}

如何手动取消?

c# asp.net-core background-service cancellation-token
3个回答
22
投票

尚不清楚您是否要取消所有服务,也许是应用程序本身(或至少是主机),还是只是单个服务。

停止应用程序

要取消应用程序,请在类中注入 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();
    }
}

4
投票

已经晚了,但只需触发 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);
        }
    }
}

1
投票

我检查了BackgroundService源代码

看来我之前的答案是错误的。

ExecuteAsync
参数是
StartAsync
方法提供的令牌。

BackgroundService
代币源由
StopAsync
方法取消。

因此,要取消

CustomService
异步工作,您必须调用
StopAsync
方法。此取消令牌作为参数提供给
ExecuteAsync
方法。

© www.soinside.com 2019 - 2024. All rights reserved.