1

I'm trying manually stop the class that inherit from BackgroundService.

ExampleService.cs

public class ExampleService : BackgroundService, IExampleService
{
    private readonly ILogger<ExampleService> _logger;
    private bool stopRequested { get; set; }

    public ExampleService(ILogger<ExampleService> logger)
    {
        _logger = logger;
    }
    public async Task Stoping(CancellationToken token)
    {
        _logger.LogInformation("Stoping");
        stopRequested = true;
        await StopAsync(token);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var count = 0;
        while (!stoppingToken.IsCancellationRequested && !stopRequested)
        {
            await Task.Delay(1000, stoppingToken);
            _logger.LogInformation("Service is working {0}", count++);
        }
    }
}
public interface IExampleService
{
    Task Stoping(CancellationToken token = default);

}

Call from API

[HttpGet("stoptask2")]
public async Task<IActionResult> Get()
{
    await exampleService.Stoping();
    return Ok();
}

But the service doesn't stop. I know IHostApplicationLifetime is working but all application is stopping. I don't want this.

I have tried How to cancel manually a BackgroundService in ASP.net core but it doesn't work.

I know I should stop the service using stoppingToken in ExecuteAsync, but how?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Okan Karadag
  • 2,542
  • 1
  • 11
  • 23
  • You need to use a cancellation token. The current call to `Stopping` is using the default value which is essentially a dummy token, i.e., `CancellationToken.None` – Metro Smurf Jan 07 '23 at 22:16
  • Seems that this was answered quite well here: https://stackoverflow.com/questions/67898449/how-to-cancel-manually-a-backgroundservice-in-asp-net-core – akseli Jan 07 '23 at 22:17
  • Does this answer your question? [How to cancel manually a BackgroundService in ASP.net core](https://stackoverflow.com/questions/67898449/how-to-cancel-manually-a-backgroundservice-in-asp-net-core) – Metro Smurf Jan 07 '23 at 22:18
  • @MetroSmurf I tried exactly the same, but don't working – Okan Karadag Jan 07 '23 at 22:21
  • The code posted shows there is no `CancellationToken` used; nothing will stop b/c the token does not signal the bg service to cancel it's operations. – Metro Smurf Jan 07 '23 at 22:24
  • @MetroSmurf I know that. I tried the solution you mentioned. I'm asking how can I stop it. – Okan Karadag Jan 07 '23 at 22:28

2 Answers2

2

Without seeing the actual registration I can only guess, but I think problem is the registration - AddHostedService creates one service descriptor and when you register IExampleService it will create another unrelated one, so you have two different instances of service. Personally I would just use the separation of concerns principle and introduce separate service like IStopper which would have two methods Stop and IStopped (or expose a cancelation toke instead of the second one), but if you want to keep your current class/interface structure - then be sure to provide registration which will result in only one instance:

builder.Services.AddSingleton<ExampleService>(); // singleton
builder.Services.AddSingleton<IExampleService>(s => s.GetRequiredService<ExampleService>()); // reabstract as interface
builder.Services.AddHostedService<ExampleService>(s => s.GetRequiredService<ExampleService>()); // reabstract as hosted service
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • so how to the method to restart service? manually starting not working. – Okan Karadag Jan 08 '23 at 18:41
  • 2
    @OkanKaradag have not tried that and not sure ATM this would be correct approach but you can try calling `StartAsync` (and overload it to set `stopRequested` to false). Or use some synchronization mechanism (based on `SemaphoreSlim` or `ReaderWriterLockSlim`) instead just ending the loop on bool. – Guru Stron Jan 08 '23 at 19:25
0

I think that the CancellationToken in the Stopping and Execute methods cannot process because they are different objects. So if you use this figure, you will solve the problem.

public class ExampleService : BackgroundService, IExampleService
{
    private readonly ILogger<ExampleService> _logger;
    private CancellationToken cancellationToken = new CancellationToken();
    private static bool stopRequested = true;

    public ExampleService(ILogger<ExampleService> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var count = 0;
        while (!stoppingToken.IsCancellationRequested)
        {
            if (!stopRequested)
            {
                await base.StopAsync(stoppingToken);
            }
            await Task.Delay(3000, stoppingToken);
            _logger.LogInformation("ExampleService is working {0}", count++);
           
           
        }
    }

    public void Stoping() => stopRequested = false;
}
}

public interface IExampleService
{
    void Stoping();
}

Program.cs

builder.Services.Configure<HostOptions>(hostOptions =>
{
    hostOptions.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore;
});
builder.Services.AddHostedService<ExampleService>();
builder.Services.AddSingleton<IExampleService,ExampleService>();
Bayram Eren
  • 422
  • 4
  • 6