I'm trying to replace my old Threading.Timer code with something that can handle an async Function. The reason I'm doing this is that you cannot pass a Threading.Timer an async function without making it an Async Sub, which I am to understand is a bad idea.
Code I wish to replace;
Dim SaveTimer as New Threading.Timer(AddressOf SaveToFile, Nothing, 1000 * 60 * 5, 1000 * 60 * 5)
SaveToFile is an async function, so I get an "The Task returned from this Async Function will be dropped, and any exceptions in it ignored. Consider changing it to an Async Sub so its exceptions are propagated" warning.
The purpose of this timer is to save the contents of the class it's in to a file every 5 minutes. However, I also occasionally call SaveToFile manually and if I do I want to reset the timer.
With the 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 it and reset.
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
What concerns me is that I call StopTimer, then shortly afterwards StartTimer, which replaces CancellationToken with a new instance. I'm worried that before the cancellation of the token takes effect I'm overwriting it.
What is the solution to this?
I know the code is in VB but I understand C# just as well, so either as an answer would be fine.
EDIT1: This needs to be thread-safe. This is the cause of my concern.
Timer ticks are queued to the thread-pool. You can use async and await there without problems. Make your tick function call an async Task function and throw away 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 execute concurrently and they can run after you have stopped the timer.
I don't see why you'd need to use async code anyway. You are already on the thread-pool so you don't have to unblock the UI. Writing to local disks does not benefit much from async IO. Maybe the answer is: Just use synchronous code here.
In order to guard against overwriting the CancellationToken you need to copy it to all places that uses it. Nothing should directly use the field.
Better yet, create a new instance of AsyncTimer instead of making it restartable. Avoid state mutation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With