用自定义异步计时器类替换Threading.Timer?

时间:2014-09-13 13:28:12

标签: .net vb.net asynchronous timer task-parallel-library

我正在尝试用可处理异步函数的东西替换旧的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:这需要是线程安全的。这是我担心的原因。

1 个答案:

答案 0 :(得分:2)

计时器标记排队到线程池。您可以毫无问题地使用asyncawait。使您的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的新实例,而不是让它可重新启动。避免状态突变。