我正在使用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
上返回,并且计时器不知道计划的继续。也就是说,上面的代码可能会在延续完成之前返回(并且回调可能会尝试更改已处置的计时器)。
首先,我的理解正确吗?
第二,等待异步回调完成的正确方法是什么?
答案 0 :(得分:2)
您的理解是正确的,您的Timer
将不会意识到您的CallbackAsync
函数仍在运行,因此您对Dispose(WaitHandle)
的调用将返回并立即向您的WaitHandle
发信号,在处置CallbackAsync
之后,到Timer.Change
的呼叫可能会呼叫Timer
。
这里有一些危险信号:
async void
。这几乎总是一个坏主意,因为异步函数应该返回一个Task,否则调用者没有任何等待的机会。Thread
和Task
范例。由于TimerCallback
不支持任务,因此在仍然使用Timer
的情况下使其正常工作的唯一方法是通过删除async/await
关键字并使用{{ 1}},直到Task.Wait
返回的Task
完成为止。正如其他人所提到的,通过创建SomethingAsync
并将其传递给CancellationToken
来添加对取消的支持也是明智的,这样,当您调用SomethingAsync
时,您可以取消正在运行的回调(此Dispose
需要由CancellationToken
传递给它调用的任何其他异步函数,并且/或者在SomethingAsync
执行时定期检查是否取消)。
编辑:如果您想转换为基于自然的SomethingAsync
的方式,请参见Run async method regularly with specified interval