我正在尝试用可处理异步函数的东西替换旧的Threading.Timer代码。我这样做的原因是你不能传递一个Threading.Timer异步函数而不使它成为Async Sub,我明白这是一个坏主意。
我希望替换代码;
Dim SaveTimer as New Threading.Timer(AddressOf SaveToFile, Nothing, 1000 * 60 * 5, 1000 * 60 * 5)
SaveToFile是一个异步函数,所以我得到一个“从这个异步函数返回的任务将被删除,其中的任何异常都被忽略。考虑将其更改为异步子,以便传播其异常”警告。
此计时器的目的是每5分钟将其所在类的内容保存到文件中。但是,我偶尔也会手动调用SaveToFile,如果我这样做,我想重置计时器。
使用Threading.Timer我会这样做;
Public Async Function ManuallySave As Tasks.Task
SaveTimer.Change(Timeout.Infinite, Timeout.Infinite)
Await SaveToFile
SaveTimer.Change(1000 * 60 * 5, 1000 * 60 * 5)
End Function
现在我知道如何安排使用Task.Delay完成任务,但我不知道如何阻止它并重置。
这是我对异步计时器类的尝试;
Public Class AsyncTimer
Private CancellationToken As New CancellationTokenSource
Private DelegateAction As Action(Of Tasks.Task) = Nothing
Public Sub New(DelegateAction As Action(Of Tasks.Task))
Me.DelegateAction = DelegateAction
End Sub
Public Async Function StartTimer(Interval As Integer, Optional Repeat As Boolean = False) As Tasks.Task
CancellationToken = New CancellationTokenSource
Do
Await Tasks.Task.Delay(Interval, CancellationToken.Token).ContinueWith(DelegateAction, CancellationToken.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Current)
Loop Until Not Repeat OrElse CancellationToken.IsCancellationRequested
End Function
Public Sub StopTimer()
CancellationToken.Cancel()
End Sub
End Class
我担心的是我调用StopTimer,之后不久调用StartTimer,它将CancellationToken替换为新实例。我担心在取消令牌生效之前我会覆盖它。
这是什么解决方案?
我知道代码在VB中,但我也理解C#,所以要么答案也不错。
EDIT1:这需要是线程安全的。这是我担心的原因。
答案 0 :(得分:2)
计时器标记排队到线程池。您可以毫无问题地使用async
和await
。使您的tick函数调用async Task
函数并丢弃生成的任务。并记录,为什么。
void TimerProc(...) {
var task = TimerImpl();
//throw away task
}
async Task TimerImpl() {
try { MyCode(); }
catch (Exception ex) { Log(ex); }
}
注意,计时器滴答可以任意延迟,它们可以同时执行,并且可以在停止计时器后运行。
我不明白为什么你还需要使用异步代码。您已经在线程池中,因此您不必取消阻止UI。写入本地磁盘不会从异步IO中获益。也许答案是:在这里使用同步代码。
为了防止覆盖CancellationToken
,您需要将其复制到使用它的所有地方。什么都不应该直接使用这个领域。
更好的是,创建AsyncTimer
的新实例,而不是让它可重新启动。避免状态突变。