Using a Timer in The Background Thread to Update UI

Background timer to update UI?

You need two things for it:

  1. Timer

    You can update the UI in System.Timers.Timer with the 10 seconds interval.

  2. Dispatcher

    You need to use Dispatcher.Invoke to change the UI without holding the main UI thread. Instead the method Process should be called on a separate thread (Timer method), other than main UI thread, and use Dispatcher in it to alert main UI thread for the change.

    Process() // method to be called after regular interval in Timer
    {
    // lengthy process, i.e. data fetching and processing etc.

    // here comes the UI update part
    Dispatcher.Invoke((Action)delegate() { /* update UI */ });
    }

Update label text with timer on UI Thread, without blocking the UI

to understand how i can run a method on my UI thread, and still interact with the UI. Therefore, using a Timer is not a possibility in this case i'm afraid.

You can use a thread (via Task.Run) and then use Device.InvokeOnMainThreadAsync or Device.BeginInvokeOnMainThread to execute UI updates within that "background" thread:

Task.Run(async() =>
{
while (true)
{
await Device.InvokeOnMainThreadAsync(() =>
{
test.Text = i;
});
await Task.Delay(10000);
i++;
}
});

Note: Also do not use Thread.Sleep as that will hang the entire thread, including its message pump (a really bad thing when it is the UI/main thread).

How to execute a method every second on a background thread so it doesn't affect the performance of the app

Use Grand Central Dispatch :

DispatchQueue.global(qos: .background).async {
getDatabaseInfo()
}

Update GUI while Other Threads are working without Thread Change - C#

There are various ways to approach this.

The first thing is to define "realtime". If your data changes every 1 millisecond, even if you were able to update the UI that fast, noone would be able to see it. As a guideline, we can only detect changes at around 60Hz (that's why videogames target that framerate). In practice, you probably want the UI to update in the 10-50Hz range.

The timer solution

A simple solution, which may or may not be appropriate, would be to setup a timer on the UI thread that fires at the appropriate rate and update your controls in the timer event handler.

The Invoke() / BeginInvoke() solution

Another option is to still use the BeginInvoke() approach, but:

  1. Implement the logic to update all controls in a single function and only BeginInvoke() that one, so you only queue a single work item in the UI thread. If you were to do a BeginInvoke() for each control, you'd cause a context switch for each control.

  2. Skip invoking a BeginInvoke() if a minimum time has not elapsed since the last update. For instance, if data has changed after 3 milliseconds, you could skip all updates until one happens after 50 milliseconds (that would give a max update rate of 20 Hz).

The complications

This will work fine if you have simple controls, however you could run into issues if you have complex ones, like graphs, of many many controls to update. In this case, it may take a long time to redraw them, so you could not be able to update the UI at the desired rate. In you BeginInvoke() too often and the UI thread can't keep up, the app will essentially freeze because it doesn't have time to handle the user input.

There could be other conditions that lead the main thread to be more busy than usual (resizing the window or other processing that takes max a couple of seconds and you didn't bother to run in a separate thread).

So, in my programs, I usually set a flag immediately before I call BeginInvoke(), and I clear it in the invoked function. The next time I have to call BeginInvoke(), I first check the flag. If it's still set, it means the UI thread was busy and still hasn't managed to update the UI. In that case, I skip the BeginInvoke().

Finally, if you have a lot of stuff going on (I had to update many graphs and views) you may also need to have your logic guarantee a minimum time from when the update code in the UI thread ends executing and when you queue a new update from your background thread. This guarantees there's some time left in the UI thread to process user input, while the thread is very busy updating the UI in the rest of the time.

Final notes

If a value has not changed, you want to avoid redrawing the relative control, because it's pointless. I expect most WinForms controls, like a label, to already not redraw if you set their Text to the same value they already have, but if you have custom controls, third party controls, or do things like clear a ListView and repopulate it, you want to make sure the code isn't causing a redraw when it's not needed.



Related Topics



Leave a reply



Submit