Replace Threading.Timer with a custom async timer class?

I am trying to replace the old Threading.Timer code with something that can handle an async function. The reason I am doing this is because you cannot pass Threading.Timer to an async function without making it an Async Sub which I have to understand is a bad idea.

The code I want to replace;

Dim SaveTimer as New Threading.Timer(AddressOf SaveToFile, Nothing, 1000 * 60 * 5, 1000 * 60 * 5)

      

SaveToFile is an asynchronous function, so I get "The task returned from this Async function will be removed and all exceptions in it will be ignored. Try changing it to Sub Async to propagate its exceptions."

The purpose of this timer is to save the contents of the class to a file every 5 minutes. However, I also sometimes call SaveToFile manually and if I want to reset the timer.

With Threading.Timer, I would do this:

  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

      

Now I know how to schedule something to be done with Tasks using Task.Delay, but I'm not sure how to stop and reset it.

Here's my attempt at an async timer class;

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

      

As for me, I call the StopTimer and then shortly thereafter StartTimer, which replaces the CancellationToken with a new instance. I am worried that before canceling the token takes effect, I overwrite it.

What is the solution to this?

I know the code is in VB, but I also understand C #, so the answer would be accurate.

EDIT1: This should be thread safe. This is the cause of my concern.

+3


source to share


1 answer


Tick-ticks are queued in the thread pool. You can use async

it await

there as well. Make the tick function a function call async Task

and discard the resulting task. And document why.

void TimerProc(...) {
 var task = TimerImpl();
 //throw away task
}

async Task TimerImpl() {
 try { MyCode(); }
 catch (Exception ex) { Log(ex); }
}

      

Note that timer ticks can be delayed arbitrarily, they can run at the same time, and they can start after you have stopped the timer.



I don't understand why you need to use async code anyway. You are already in the thread pool, so you don't need to unlock the UI. Writing to local disks doesn't benefit much from asynchronous I / O. Perhaps the answer is: just use synchronous code here.

To protect it from being overwritten CancellationToken

, you need to copy it to all the locations it uses. Nothing should directly use the field.

Better yet, create a new instance AsyncTimer

instead of restarting it. Avoid mutating the condition.

+2


source







All Articles