Как реализовать размещенный сервис для одноэтапного задания, которое можно запускать и останавливать? - PullRequest
1 голос
/ 16 октября 2019

Я хочу создать размещенный сервис, который можно запускать и останавливать через взаимодействие с пользователем. Служба должна не запускаться автоматически, попытки запустить ее, когда она уже запущена, не должны иметь никакого эффекта, и должна быть возможность проверить, работает ли служба в настоящее время. Служба в конечном итоге завершит работу, после чего она должна перейти в состояние останова, пока не будет запущена снова.

Ниже приведена моя попытка реализовать это, и она, похоже, работает, но мне было интересно, есть лилюбые явные недостатки этого подхода или если есть более простое решение.

Есть две части: синглтон 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);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...