1

我想创建一个可以通过用户交互启动和停止的托管服务。该服务应该自动启动,在它已经启动时尝试启动它应该没有效果,并且应该可以检查该服务当前是否正在运行。该服务最终将完成运行,此时它应该转换到停止状态,直到它再次启动。

下面是我实现这一点的尝试,它似乎正在工作,但我想知道这种方法是否存在任何明显的缺陷,或者是否有更简单的解决方案。

有两个部分,一个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);
        }
    }
}
4

0 回答 0