Я хочу создать размещенный сервис, который можно запускать и останавливать через взаимодействие с пользователем. Служба должна не запускаться автоматически, попытки запустить ее, когда она уже запущена, не должны иметь никакого эффекта, и должна быть возможность проверить, работает ли служба в настоящее время. Служба в конечном итоге завершит работу, после чего она должна перейти в состояние останова, пока не будет запущена снова.
Ниже приведена моя попытка реализовать это, и она, похоже, работает, но мне было интересно, есть лилюбые явные недостатки этого подхода или если есть более простое решение.
Есть две части: синглтон FooJobControl
, который может быть введен в контроллеры для запуска или остановки службы, и размещенная служба FooJob
который ждет сигнала от FooJobControl
для начала работы и сигнализирует о его остановке.
в Startup.cs
services.AddHostedService<FooJob>();
services.AddSingleton<FooJobControl>();
FooJobControl.cs
public class FooJobControl
{
private AsyncAutoResetEvent _evt = new AsyncAutoResetEvent(false);
private CancellationTokenSource _cts;
private object _sync = new object();
public void Start()
{
lock (_sync)
{
if (_cts == null)
{
_evt.Set();
}
}
}
public void Stop()
{
lock (_sync)
{
if (_cts != null)
{
_cts.Cancel();
}
}
}
public bool IsRunning
{
get
{
lock (_sync)
{
return _cts != null && !_cts.IsCancellationRequested;
}
}
}
public async Task<CancellationToken> JobStart(CancellationToken stoppingToken)
{
await _evt.WaitAsync(stoppingToken);
lock (_sync)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
return _cts.Token;
}
}
public void JobEnd()
{
lock (_sync)
{
_cts = null;
}
}
}
FooJob.cs
public class FooJob : BackgroundService
{
private FooJobControl _control;
private ILogger<FooJob> _logger;
public FooJob(ILogger<FooJob> logger, FooJobControl control)
{
_logger = logger;
_control = control;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
CancellationToken token = await _control.JobStart(stoppingToken);
try
{
await DoWork(token);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to run job");
}
_control.JobEnd();
}
}
private async Task DoWork(CancellationToken token)
{
for (int i = 1; i <= 10; i++)
{
if (token.IsCancellationRequested)
{
_logger.LogInformation("DoWork Cancelled");
return;
}
_logger.LogInformation("DoWork " + i);
await Task.Delay(500);
}
}
}