Как поймать, что Task.Delay () отменен и сделать обратный вызов? - PullRequest
0 голосов
/ 18 апреля 2019

Допустим, у меня есть рабочий класс.

public sealed class Worker : IDisposable
{
    private bool _isRunning;
    private CancellationTokenSource _cts;

    private readonly Action _action;
    private readonly int _millisecondsDelay;
    private readonly object _lock = new object();

    public Worker(Action action, int millisecondsDelay)
    {
        _action = action;
        _millisecondsDelay = millisecondsDelay = 5000;
    }

    public void Start()
    {
        lock (_lock)
        {
            if (!_isRunning)
            {
                _isRunning = true;
                Run();
            }
        }
    }

    public void Cancel()
    {
        lock (_lock)
        {
            if (_isRunning) _cts.Cancel();
        }
    }

    private void Run()
    {
        using (_cts) _cts = new CancellationTokenSource();
        Task.Run(async () => { await DoAsync(_cts.Token); });
    }

    private async Task DoAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            //Log.Message1("____REFRESHING STATUS____");
            _action();
            await Task.Delay(_millisecondsDelay, cancellationToken);
        }

        //this code is unreachable
        lock (_lock)
        {
            _isRunning = false;
        }
    }

    public void Dispose()
    {
        try
        {
            _cts?.Cancel();
        }
        finally
        {
            if (_cts != null)
            {
                _cts.Dispose();
                _cts = null;
            }
        }
    }
}

Проблема в том, что код _isRunning = false; недоступен.Я имею в виду более вероятно, когда вызывающий вызовет Cancel метод, рабочий будет ожидать Task.Delay.Итак, как я могу вызвать что-то (здесь это _isRunning = false;) после того, как моя задача будет отменена?Другими словами, я должен быть уверен, что мой работник не работает (это не отмененное состояние)

1 Ответ

3 голосов
/ 18 апреля 2019

Чтобы ответить на буквальный вопрос, вы можете использовать блок finally:

private async Task DoAsync(CancellationToken cancellationToken)
{
  try
  {
    while (!cancellationToken.IsCancellationRequested)
    {
      //Log.Message1("____REFRESHING STATUS____");
      _action();
      await Task.Delay(_millisecondsDelay, cancellationToken);
    }
  }
  finally
  {
    lock (_lock)
    {
      _isRunning = false;
    }
  }
}

Но у меня есть некоторые опасения по поводу этого "рабочего" подхода:

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

Возможно, стоит отступить и пересмотреть то, что вы на самом деле хотите сделать с этим кодом.

...