Я создал новый проект, используя шаблон службы worker, и добавил две строки.
Один дополнительный вывод журнала в worker после остановки:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
_logger.LogInformation("Worker stopped."); // <---- extra log output
}
И один в Program.cs для поддержки systemd:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSystemd() // <---- systemd support
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
Я могу запускать и останавливать службу на моем сервере ubuntu, но в выводе journalctl не появляется дополнительная строка журнала:
worker[25468]: WorkerService.Worker[0] Worker running at: 02/10/2021 23:07:34 +01:00
systemd[1]: Stopping Long running service/daemon created from .NET worker template...
worker[25468]: Microsoft.Hosting.Lifetime[0] Application is shutting down...
// here I would expect "Worker stopped."
systemd[1]: Stopped Long running service/daemon created from .NET worker template.
Как я могу убедиться, что моя фоновая служба работает до завершения до остановки службы?
Я нашел этот очень похожий вопрос, но, к сожалению, на него нет удовлетворительного ответа: . NET core BackgroundService не завершается корректно как демон
Но я не могу поверить, что на этот вопрос нет однозначного ответа.
ОБНОВЛЕНИЕ:
Я нашел способ изящно закрыть службу. Вместо того, чтобы ждать, пока сработает stoppingToken
в ExecuteAsync()
, я проверяю флаг, должна ли служба перестать работать, который установлен в методе StopAsync()
. Затем с помощью ManualResetEvent
я могу решить, должна ли служба ждать, пока Backgroundservice завершит свою работу и корректно завершит свою работу, или она должна принудительно завершить работу по истечении заданного тайм-аута. Вероятно, это не самый элегантный способ сделать это, но, похоже, он делает то, что я хочу.
private bool _stopRunning;
private ManualResetEvent _mre;
private int _graceperiod = 5000;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_stopRunning = false;
_mre = new ManualResetEvent(false);
int item = 1;
while (!_stopRunning)
{
_logger.LogInformation("Working on item " + item);
await Task.Delay(3000);
_logger.LogInformation("still working on item " + item);
await Task.Delay(3000);
_logger.LogInformation("Finished working on item " + item);
item++;
}
_logger.LogInformation("Worker stopped.");
_mre.Set();
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
_stopRunning = true;
bool gracefulShutdown = _mre.WaitOne(_graceperiod);
if (gracefulShutdown)
{
_logger.LogInformation("Shut down service gracefully");
}
else
{
_logger.LogInformation("Force shut down service");
}
await base.StopAsync(cancellationToken);
}
Я все же хотел бы понять, как должен срабатывать stoppingToken.