【问题标题】:How to wait for async timer callbacks如何等待异步计时器回调
【发布时间】:2019-11-27 20:24:17
【问题描述】:

我正在使用 System.Threading.Timer 运行一些 async 的东西

这意味着我的回调是async void

private async void CallbackAsync(object state) {
    _timer.Change(Timeout.Infinite, Timeout.Infinite);
    await SomethingAsync();
    _timer.Change(TimeToNextCallback, Timeout.Inifinite);
}

对于非异步回调,我使用了这样的代码来安全地关闭计时器。

using (var waitHandle = new AutoResetEvent(false)) {
    if (_timer.Dispose(waitHandle))        {
        waitHandle.WaitOne();
    }
}

我的理解是这不适用于异步回调。回调将在第一个 await 上返回,并且 Timer 不知道预定的延续。即上面的代码可能会在延续完成之前返回(并且回调可能会尝试更改已处置的计时器)。

首先,我的理解正确吗?

其次,等待异步回调完成的正确方法是什么?

【问题讨论】:

  • 标准 cancel task 似乎是一条可能的路线...而且我会完全放弃计时器,只需 await Task.Delay(canelationToken)...
  • 你能描述一下你想通过使用计时器来实现什么吗?
  • 抱歉,回复晚了。计时器是我正在尝试更新以支持异步回调的旧代码。

标签: c# timer async-await


【解决方案1】:

您的理解是正确的,您的Timer 将不知道您的CallbackAsync 函数仍在运行,因此您对Dispose(WaitHandle) 的调用将立即返回并通知您的WaitHandle,此时调用@987654326 @ 可以在 Timer 被释放后调用 Timer.Change

这里有一些危险信号:

  1. 您正在使用async void。这几乎总是一个坏主意,因为异步函数应该返回一个任务,否则调用者没有什么可以等待的。
  2. 您正在混合使用 ThreadTask 范式。

因为TimerCallback 不支持任务,在仍然使用Timer 的同时使其正常工作的唯一方法是通过删除async/await 关键字并使用Task.Wait 阻止直到@ SomethingAsync 返回的 987654336@ 已完成。正如其他人所提到的,通过创建CancellationToken 并将其传递给SomethingAsync 添加对取消的支持也是明智的,这样当您调用Dispose 时,您将能够取消正在运行的回调(@987654341 @ 需要由 SomethingAsync 传递给它调用的任何其他异步函数和/或在 SomethingAsync 执行时定期检查取消)。

编辑:如果您想改用基于Task 的更自然的方式,请参阅Run async method regularly with specified interval

【讨论】:

  • 希望对现有代码进行简单更改以支持异步回调。似乎需要重写以使用 Task.Delay 。感谢您的意见。
  • 如果您必须使用 Timer,最简单的解决方案是放弃 async/await 并在异步代码返回的任务上调用 .Wait()
猜你喜欢
  • 2021-06-30
  • 2021-10-12
  • 1970-01-01
  • 1970-01-01
  • 2019-12-19
  • 2021-11-13
相关资源
最近更新 更多