【问题标题】:How to catch that Task.Delay() canceled and do a callback?如何捕获 Task.Delay() 取消并进行回调?
【发布时间】:2019-04-18 11:07:35
【问题描述】:

假设我有一个工人阶级。

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;)?换句话说,我需要确保我的工人没有运行(它不是取消状态)

【问题讨论】:

  • 这适用于 ASP.NET、ASP.NET Core、WPF 还是其他特定平台?其中一些为具有启动/停止功能的后台任务/进程提供了特定的方法。
  • @FireLancer 适用于 wpf 平台
  • DispatcherTimer 可能是相关的,如果您想从计时器访问 UI。
  • 可能是,但我根本不喜欢使用计时器

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


【解决方案1】:

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

也许值得退后一步,重新考虑一下你真正想要用这段代码做什么。

【讨论】:

  • 好的。谢谢,它有效。基本上我只需要定期(每秒/分钟一次)调用一个方法,您可以建议作为更好的方法吗?
  • _isRunning 这里看起来很像检查Task 对象的状态(这也是可以等待的)?此外,如果发生这种情况,Task 提供了一种查看失败的方法,而布尔标志则没有。
  • @isxaker:计时器怎么样?
  • @StephenCleary 计时器,真的吗?至少在我看来,计时器已经过时了。我也发现了这篇文章stackoverflow.com/questions/13695499/…
  • @FireLancer 实际上这个标志表示两个任务(动作和延迟)的联合是否工作。我的行动有点安全 - Action action = ()=>{try{}catch{}};
猜你喜欢
  • 1970-01-01
  • 2021-09-07
  • 2020-03-26
  • 1970-01-01
  • 2015-12-02
  • 2014-09-07
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多