如何捕获已取消的Task.Delay()并执行回调?

时间:2019-04-18 11:07:35

标签: c# .net async-await task-parallel-library

让我们说我有一个工人阶级。

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。因此,在取消任务后如何调用smth(此处为_isRunning = false;)?换句话说,我需要确保我的工作程序没有运行(这不是取消状态)

1 个答案:

答案 0 :(得分:3)

要回答字面上的问题,可以使用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与异步代码混合可能会出现问题。您应该绝对确定这是您真正想要做的。

可能值得退后一步,重新考虑您实际上要使用此代码执行的操作。