Async friendly DispatcherTimer wrapper/subclass - c#

I have a DispatcherTimer running in my code that fire every 30 seconds to update system status from the server. The timer fires in the client even if I'm debugging my server code so if I've been debugging for 5 minutes I may end up with a dozen timeouts in the client. Finally decided I needed to fix this so looking to make a more async / await friendly DispatcherTimer.
Code running in DispatcherTimer must be configurable whether it is reentrant or not (i.e. if the task is already running it should not try to run it again)
Should be task based (whether or not this requires I actually expose Task at the root is a gray area)
Should be able to run async code and await on tasks to complete
Whether it wraps or extends DispatcherTimer probably doesn't really matter but wrapping it may be slightly less ambiguous if you don't know how to use it
Possibly expose bindable properties for IsRunning for UI

Here's what I came up with.
SmartDispatcherTimer Extends DispatcherTimer (was easiest way to get this up and running)
Has a TickTask property to provide a Task to handle the logic
Has an IsReentrant property (of course the whole point is that I want it to not be reentrant so normally this is false)
It assumes anything you're calling is fully awaitable - or you'd end up losing the reentrancy protection benefits
Usage:
var timer = new SmartDispatcherTimer();
timer.IsReentrant = false;
timer.Interval = TimeSpan.FromSeconds(30);
timer.TickTask = async () =>
{
StatusMessage = "Updating..."; // MVVM property
await UpdateSystemStatus(false);
StatusMessage = "Updated at " + DateTime.Now;
};
timer.Start();
Here's the code. Would love to hear any thoughts on it
public class SmartDispatcherTimer : DispatcherTimer
{
public SmartDispatcherTimer()
{
base.Tick += SmartDispatcherTimer_Tick;
}
async void SmartDispatcherTimer_Tick(object sender, EventArgs e)
{
if (TickTask == null)
{
Debug.WriteLine("No task set!");
return;
}
if (IsRunning && !IsReentrant)
{
// previous task hasn't completed
Debug.WriteLine("Task already running");
return;
}
try
{
// we're running it now
IsRunning = true;
Debug.WriteLine("Running Task");
await TickTask.Invoke();
Debug.WriteLine("Task Completed");
}
catch (Exception)
{
Debug.WriteLine("Task Failed");
}
finally
{
// allow it to run again
IsRunning = false;
}
}
public bool IsReentrant { get; set; }
public bool IsRunning { get; private set; }
public Func<Task> TickTask { get; set; }
}

Related

How to keep a System.Timers.Timer stopped during ElapsedEvent

I am using System.Timers.Timer and every x seconds I need to perform some tasks in an ElapsedEvent method. While I am performing my tasks in the ElapsedEvent method, I want the timer to be stopped. However, I have another method that can start the timer, which can be called while the ElapsedEvent is running. My code looks something like this:
class MyClass {
Timer myTimer;
public MyClass {
myTimer = new System.Timers.Timer();
// init timer code here...
}
public void ElapsedEventTask(object source, ElapsedEventArgs e) {
myTimer.Enabled = false;
try
{
// do my tasks
}
catch
{
...
}
finally
{
myTimer.Enabled = true;
}
}
}
public void AnotherMethod() {
// do some things
myTimer.Enabled = true;
}
How do I prevent AnotherMethod from starting the timer while I'm completing the task in ElapsedEventTask?
You can add a variable that indicate if the task is running. Finaly to be thread safe, you need to use lock when this variable is used in with myTimer.Enabled :
class MyClass
{
object syncEnableRunning = new object();
bool running
Timer myTimer;
public void ElapsedEventTask(object source, ElapsedEventArgs e)
{
lock(syncEnableRunning)
{
running = true;
myTimer.Enabled = false;
}
try { /*do my tasks*/}
catch { ... }
finally
{
lock(syncEnableRunning)
{
myTimer.Enabled = true;
running = false;
}
}
}
public void AnotherMethod()
{
// do some things
lock(syncEnableRunning)
{
if(!running)
{
myTimer.Enabled = true;
}
}
}
}
According to the documentation the System.Timers.Timer class is not thread-safe, so it's not safe to touch its Enabled property from multiple threads without synchronization (doing so results to undefined behavior). Vernou's answer shows how to synchronize the threads by using locks, but personally I am a bit nervous with trying to enforce a non-overlapping execution policy using a mechanism that apparently was designed to be re-entrant. So my suggestion is to ditch the System.Timers.Timer, and use instead an asynchronous loop, controlled by Stephen Cleary's PauseTokenSource mechanism:
class MyClass
{
private readonly CancellationTokenSource _cts;
private readonly PauseTokenSource _pts;
public Task Completion { get; private set; }
public MyClass(TimeSpan interval)
{
_cts = new CancellationTokenSource();
_pts = new PauseTokenSource();
_pts.IsPaused = true;
Completion = Task.Run(async () =>
{
try
{
while (true)
{
await _pts.Token.WaitWhilePausedAsync(_cts.Token);
var delayTask = Task.Delay(interval, _cts.Token);
/* Do my tasks */
await delayTask;
}
}
catch (OperationCanceledException)
when (_cts.IsCancellationRequested) { } // Ignore
});
}
public void Start() => _pts.IsPaused = false;
public void Stop() => _pts.IsPaused = true;
public void Complete() => _cts.Cancel();
}
The PauseTokenSource is the controller of a PauseToken, a similar concept with the CancellationTokenSource/CancellationToken combo. The difference is that the CancellationTokenSource can be canceled only once, while the PauseTokenSource can be paused/unpaused multiple times. This class is included in the AsyncEx.Coordination package.
The MyClass exposes a Complete method that terminates the asynchronous loop, and a Completion property that can be awaited. It is a good idea to await this property before closing the program, to give to any active operation the chance to complete. Otherwise the process may be killed in the middle of a background execution, with unpredictable consequences.
I would create a one shot timer, which you then need to start again at the end of your timer function.
myTimer = new System.Timers.Timer();
myTimer.AutoReset = false;
public void ElapsedEventTask(object source, ElapsedEventArgs e) {
...
finally
{
myTimer.Start();
}
}

UWP Update UI From Async Worker

I am trying to implement a long-running background process, that periodically reports on its progress, to update the UI in a UWP app. How can I accomplish this? I have seen several helpful topics, but none have all of the pieces, and I have been unable to put them all together.
For example, consider a user who picks a very large file, and the app is reading in and/or operating on the data in the file. The user clicks a button, which populates a list stored on the page with data from the file the user picks.
PART 1
The page and button's click event handler look something like this:
public sealed partial class MyPage : Page
{
public List<DataRecord> DataRecords { get; set; }
private DateTime LastUpdate;
public MyPage()
{
this.InitializeComponent();
this.DataRecords = new List<DataRecord>();
this.LastUpdate = DateTime.Now;
// Subscribe to the event handler for updates.
MyStorageWrapper.MyEvent += this.UpdateUI;
}
private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
StorageFile pickedFile = // … obtained from FileOpenPicker.
if (pickedFile != null)
{
this.DataRecords = await MyStorageWrapper.GetDataAsync(pickedFile);
}
}
private void UpdateUI(long lineCount)
{
// This time check prevents the UI from updating so frequently
// that it becomes unresponsive as a result.
DateTime now = DateTime.Now;
if ((now - this.LastUpdate).Milliseconds > 3000)
{
// This updates a textblock to display the count, but could also
// update a progress bar or progress ring in here.
this.MessageTextBlock.Text = "Count: " + lineCount;
this.LastUpdate = now;
}
}
}
Inside of the MyStorageWrapper class:
public static class MyStorageWrapper
{
public delegate void MyEventHandler(long lineCount);
public static event MyEventHandler MyEvent;
private static void RaiseMyEvent(long lineCount)
{
// Ensure that something is listening to the event.
if (MyStorageWrapper.MyEvent!= null)
{
// Call the listening event handlers.
MyStorageWrapper.MyEvent(lineCount);
}
}
public static async Task<List<DataRecord>> GetDataAsync(StorageFile file)
{
List<DataRecord> recordsList = new List<DataRecord>();
using (Stream stream = await file.OpenStreamForReadAsync())
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
// Does its parsing here, and constructs a single DataRecord …
recordsList.Add(dataRecord);
// Raises an event.
MyStorageWrapper.RaiseMyEvent(recordsList.Count);
}
}
}
return recordsList;
}
}
The code for the time check I got from following this.
As written, this code makes the app unresponsive with a large file (I tested on a text file on the order of about 8.5 million lines). I thought adding async and await to the GetDataAsync() call would prevent this? Does this not do its work on a thread aside from the UI thread? Through Debug mode in Visual Studio, I have verified the program is progressing as expected... it is just tying up the UI thread, making the app unresponsive (see this page from Microsoft about the UI thread and asynchronous programming).
PART 2
I have successfully implemented before an asynchronous, long-running process that runs on a separate thread AND still updates the UI periodically... but this solution does not allow for the return value - specifically the line from PART 1 that says:
this.DataRecords = await MyStorageWrapper.GetDataAsync(pickedFile);
My previous, successful implementation follows (most of the bodies cut out for brevity). Is there a way to adapt this to allow for return values?
In a Page class:
public sealed partial class MyPage : Page
{
public Generator MyGenerator { get; set; }
public MyPage()
{
this.InitializeComponent();
this.MyGenerator = new Generator();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
this.MyGenerator.ProgressUpdate += async (s, f) => await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate ()
{
// Updates UI elements on the page from here.
}
this.MyGenerator.Start();
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
this.MyGenerator.Stop();
}
}
And in the Generator class:
public class Generator
{
private CancellationTokenSource cancellationTokenSource;
public event EventHandler<GeneratorStatus> ProgressUpdate;
public Generator()
{
this.cancellationTokenSource = new CancellationTokenSource();
}
public void Start()
{
Task task = Task.Run(() =>
{
while(true)
{
// Throw an Operation Cancelled exception if the task is cancelled.
this.cancellationTokenSource.Token.ThrowIfCancellationRequested();
// Does stuff here.
// Finally raise the event (assume that 'args' is the correct args and datatypes).
this.ProgressUpdate.Raise(this, new GeneratorStatus(args));
}
}, this.cancellationTokenSource.Token);
}
public void Stop()
{
this.cancellationTokenSource.Cancel();
}
}
Finally, there are two supporting classes for the ProgressUpdate event:
public class GeneratorStatus : EventArgs
{
// This class can contain a handful of properties; only one shown.
public int number { get; private set; }
public GeneratorStatus(int n)
{
this.number = n;
}
}
static class EventExtensions
{
public static void Raise(this EventHandler<GeneratorStatus> theEvent, object sender, GeneratorStatus args)
{
theEvent?.Invoke(sender, args);
}
}
It is key to understand that async/await does not directly say the awaited code will run on a different thread. When you do await GetDataAsync(pickedFile); the execution enters the GetDataAsync method still on the UI thread and continues there until await file.OpenStreamForReadAsync() is reached - and this is the only operation that will actually run asynchronously on a different thread (as file.OpenStreamForReadAsync is actually implemented this way).
However, once OpenStreamForReadAsync is completed (which will be really quick), await makes sure the execution returns to the same thread it started on - which means UI thread. So the actual expensive part of your code (reading the file in while) runs on UI thread.
You could marginally improve this by using reader.ReadLineAsync, but still, you will be returning to UI thread after each await.
ConfigureAwait(false)
The first trick you want to introduce to resolve this problem is ConfigureAwait(false).
Calling this on an asynchronous call tells the runtime that the execution does not have to return to the thread that originally called the asynchronous method - hence this can avoid returning execution to the UI thread. Great place to put it in your case is OpenStreamForReadAsync and ReadLineAsync calls:
public static async Task<List<DataRecord>> GetDataAsync(StorageFile file)
{
List<DataRecord> recordsList = new List<DataRecord>();
using (Stream stream = await file.OpenStreamForReadAsync().ConfigureAwait(false))
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
string line = await reader.ReadLineAsync().ConfigureAwait(false);
// Does its parsing here, and constructs a single DataRecord …
recordsList.Add(dataRecord);
// Raises an event.
MyStorageWrapper.RaiseMyEvent(recordsList.Count);
}
}
}
return recordsList;
}
Dispatcher
Now you freed up your UI thread, but introduced yet another problem with the progress reporting. Because now MyStorageWrapper.RaiseMyEvent(recordsList.Count) runs on a different thread, you cannot update the UI in the UpdateUI method directly, as accessing UI elements from non-UI thread throws synchronization exception. Instead, you must use UI thread Dispatcher to make sure the code runs on the right thread.
In the constructor get reference to the UI thread Dispatcher:
private CoreDispatcher _dispatcher;
public MyPage()
{
this.InitializeComponent();
_dispatcher = Window.Current.Dispatcher;
...
}
Reason to do it ahead is that Window.Current is again accessible only from the UI thread, but the page constructor definitely runs there, so it is the ideal place to use.
Now rewrite UpdateUI as follows
private async void UpdateUI(long lineCount)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// This time check prevents the UI from updating so frequently
// that it becomes unresponsive as a result.
DateTime now = DateTime.Now;
if ((now - this.LastUpdate).Milliseconds > 3000)
{
// This updates a textblock to display the count, but could also
// update a progress bar or progress ring in here.
this.MessageTextBlock.Text = "Count: " + lineCount;
this.LastUpdate = now;
}
});
}

DeadLock on task.Wait() with Task which edit UI

I'm trying to find some solutions to my problem here, but with no result (or I just do not get them right) so if anyone could help / explain i will be really gratefull.
I'm just developing a tool for system administrators using Win Form and now I need to create a continuous ping on the selected machine which is running on the background. There is an indicator for Online status on UI which I need to edit with background ping. So right now I'm in this state:
Class A (Win form):
ClassB activeRelation = new ClassB();
public void UpdateOnline(Relation pingedRelation)
{
//There is many Relations at one time, but form shows Info only for one...
if (activeRelation == pingedRelation)
{
if (p_Online.InvokeRequired)
{
p_Online.Invoke(new Action(() =>
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure
));
}
else
{
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure;
}
}
}
//Button for tunring On/Off the background ping for current machine
private void Btn_PingOnOff_Click(object sender, EventArgs e)
{
Button btn = (sender is Button) ? sender as Button : null;
if (btn != null)
{
if (activeRelation.PingRunning)
{
activeRelation.StopPing();
btn.Image = Properties.Resources.Switch_Off;
}
else
{
activeRelation.StartPing(UpdateOnline);
btn.Image = Properties.Resources.Switch_On;
}
}
}
Class B (class thats represent relation to some machine)
private ClassC pinger;
public void StartPing(Action<Relation> action)
{
pinger = new ClassC(this);
pinger.PingStatusUpdate += action;
pinger.Start();
}
public void StopPing()
{
if (pinger != null)
{
pinger.Stop();
pinger = null;
}
}
Class C (background ping class)
private bool running = false;
private ClassB classb;
private Task ping;
private CancellationTokenSource tokenSource;
public event Action<ClassB> PingStatusUpdate;
public ClassC(ClassB classB)
{
this.classB = classB;
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ping = PingAction(token);
running = true;
}
public void Stop()
{
if (running)
{
tokenSource.Cancel();
ping.Wait(); //And there is a problem -> DeadLock
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
private async Task PingAction(CancellationToken ct)
{
bool previousResult = RemoteTasks.Ping(classB.Name);
PingStatusUpdate?.Invoke(classB);
while (!ct.IsCancellationRequested)
{
await Task.Delay(pingInterval);
bool newResult = RemoteTasks.Ping(classB.Name);
if (newResult != previousResult)
{
previousResult = newResult;
PingStatusUpdate?.Invoke(classB);
}
}
}
So the problem is in deadlock when I cancel token and Wait() for task to complete -> it's still running, but While(...) in task is finished right.
You have a deadlock because ping.Wait(); blocks UI thread.
You should wait for task asynchronously using await.
So, if Stop() is event handler then change it to:
public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
If it is not:
public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
As mentioned by #JohnB async methods should have Async suffix so, the method should be named as StopAsync().
Similar problem and solution are explained here - Do Not Block On Async Code
You should avoid synchronous waiting on tasks, so you should always use await with tasks instead of Wait() or Result. Also, as pointed by #Fildor you should use async-await all the way to avoid such situations.

Method blocking UI thread

I have recently created a helper method to throttle the speed of property changed event in order to save the data to my local database with a given delay. The only problem I got with it is that the method is apparently not running asynchronously and blocking my UI thread.
To test it, I have attached the event in my SettingsViewModel to the PercentageSliderViewModel:
public class SettingsViewModel : BaseVM
{
public PercentageSliderViewModel Activity { get; set; }
[...]
public SettingsViewModel() {
Activity.PropertyChanged += CreateThrottledCallback(Save, 1000);
}
[...]
public async Task Save()
{
// Save data
}
}
This is my helper method:
public PropertyChangedEventHandler CreateThrottledCallback(
Func<Task> callback, int throttle = 1000)
{
bool throttling = false;
bool callFinal = false;
return async(s, e) =>
{
if (throttling)
{
callFinal = true;
return;
}
throttling = true;
await callback?.Invoke();
await Task.Delay(throttle).ContinueWith(_ => throttling = false);
if (callFinal)
{
await callback?.Invoke().ContinueWith(_ => callFinal = false);
}
};
}
Even though the throttling is working fine, when I move the slider from left to right and the callback occurs it is 'freezing' for a small amount of time.
Debugging shows that it is running on the UI thread.
How can run CreateThrottledCallback method asynchronously so it wont block my main thread then?
It's unclear what your Save method really does and whether it's actually a truly asynchronous method. You could try to execute it an a background thread in your CreateThrottledCallback method:
if (callback != null)
await Task.Run(() => callback.Invoke());
As it has nothing to do with UI, you can use ConfigureAwait(false). This will avoid capturing the current synchronization context and the task will run on TaskScheduler.Default thread pool context.
await callback().ConfigureAwait(false);

UWP ThreadPoolTimer wait until work item finished

I need to implement a periodic timer (10 seconds), that will check for some changes and update Database and UI. I need to wait until "Job" is finished and then start timer again. I call
timer.Cancel()
on "Job" starting and then create timer again on finish. But seems when I use work-item handlers that use the async keyword ,the thread pool work item may be set to the complete state before all of the code in the handler has executed.
See full code :
private bool IsStop = false;
private async Task DoWork()
{
try
{
if (IsStop)
{
Stop();
return;
}
timer.Cancel();
var resultIds = await DB.getUnresolvedData();
if (resultIds.IsNullOrEmpty())
{
return;
}
var json = await server.RequestSTatus(resultIds);
if (string.IsNullOrEmpty(json))
return;
Model result = JsonConvert.DeserializeObject<Model>(json);
UpdateDB(result);
}
catch (Exception ex)
{
}
finally
{
Start();
}
}
public void Start()
{
if (IsStop) return;
timer = ThreadPoolTimer.CreatePeriodicTimer(async (t) =>
{
await DoWork();
}, TimeSpan.FromSeconds(10));
}
The question is ,how I can wait until "Job" is finished and then start again ?
You can do it little bit different. but code will be more clear and you will use ThreadPool anyway. Just use Task.Delay for timer. Something like this.
private bool isRunning;
public async void Start()
{
if (isRunning)
return;
isRunning = true;
while (isRunning)
{
await DoWork();
//wait period.
await Task.Delay(TimeSpan.FromSeconds(10));
if (!isRunning)
return;
}
}
private async Task DoWork()
{
//Do your work here.
}
public void Stop()
{
isRunning = false;
}
It is just a general idea.

Categories

Resources