Stefano
Stefano

Reputation: 344

Stop process using CancellationToken

I need to run different instances of a Worker Service (BackgroundService): same code but different configuration. The number of instances and the start and stop of every instance will change during the running process.

So my choice was to write 2 programs:

Sample code of WorkerProgram:

var host = Host.CreateDefaultBuilder()
    .ConfigureServices(services =>
    {
        services.AddSingleton(new Instance { Id = int.Parse(args[0])});
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();

public class Worker : BackgroundService
{
    private readonly Instance _instance;
    public Worker(Instance instance)
    {
        _instance = instance;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Running {}", _instance.Id);
            await Task.Delay(10000, stoppingToken);
            _logger.LogInformation("END");
        }
    }
}

MainProgram starts every WorkerProgram using

var process = new Process();
process.StartInfo.Filename = "WorkerProgram.exe";
process.StartInfo.Arguments = i.ToString();
process.Start();

so every Worker Service run in a different process.

But when it needs to stop them, the command

process.Kill();

will Kill the process in the middle of run.

How can I stop the process in a better way (eg. using CancellationToken)?

Thank you!

My current (bad) solution is to create a placeholder file for every BackgroundService. And check the existence of the file at every cicle.

Upvotes: 3

Views: 766

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062780

untested, but:

cancellationToken.ThrowIfCancellationRequested();
process.Start();
using (cancellationToken.Register(static state => {
    try {
        (state as Process)?.Close(); // or Kill, or both; your choice
    } catch (Exception ex) {
        // best efforts only
        Debug.WriteLine(ex.Message);
    }
}, process))
{
    await process.WaitForExitAsync(cancellationToken);
}

The Register here will deal with killing the process in the cancellation case; if the external process exits before then, we will unregister via the using after the WaitForExitAsync, and won't attempt to kill it.

Upvotes: 0

Related Questions