Why Does It Take Such a Long Time for UI to Be Updated from Background Thread

Why does it take such a long time for UI to be updated from background thread?

If you attempt to do UI updates from a background thread, "the results are undefined." The most common effect I've seen is what you describe - very long delays before the update shows up. The second-most common effect I've seen is a crash. The third-most common effect is some sort of drawing artifact.

The results of doing UI updates from a background thread are truly nondeterministic. You've got multiple processor cores accessing the same hardware resources at the same time, and with the exact timing between those accesses being unknowable and infinitely variable. It would be like having a computer with no display but 2 keyboards and 2 mice, and 2 operators editing the same document at the same time. Each person's actions with the keyboard would change the state of the document, and screw up the changes the other person was trying to apply. The cursor would be in the wrong place. The amount of text in the document would be different than expected. The scroll position would be off. etc, etc.

Similarly, if 2 cores are each trying to access hardware resources to do screen refreshes, those accesses will cross and conflict with each other.

As Martin says in his comment, the UIKit code is proprietary, so we can't know the details of what goes wrong. All we know is that bad things happen, so DON'T DO THAT.

Problem with updating UI with background thread

Undoubtedly, you should use different thread for heavy processes in java. But, Anything related to UI changes, initializations like Views, Animations should be done in UI(Main) Thread which is created with your application.

You could also do:

 new Thread(new Runnable() {
@Override
public void run() {
final Bitmap bitmap =
processBitMap("image.png");

new Handler.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});

}

Even if you do a background job in another thread, Handler guarantees, setting image is done in UI/Main thread.

I can update UI from background thread, why?

I can update UI from background thread, why?

Most probably you can do that when running outside the debugger. This is because that protection is controlled by the Control.CheckForIllegalCrossThreadCalls Property. Let take a look at reference source

private static bool checkForIllegalCrossThreadCalls = Debugger.IsAttached;
public static bool CheckForIllegalCrossThreadCalls {
get { return checkForIllegalCrossThreadCalls; }
set { checkForIllegalCrossThreadCalls = value; }
}

As you may see, this protection is enabled by default only when you are debugging.

If you add the following line in your Main method (before Application.Run)

Control.CheckForIllegalCrossThreadCalls = true;

your code will not work anymore.

Background thread takes more time than UI thread

You should probably profile this to see when the execution of that background thread actually starts, and what it's actual time-consumption is - start to finish. There're a number of pretty decent profilers that can do this for you. Remember, when you create a separate thread, that doesn't mean that it necessarily fires up right at that instant. And something might be interrupting it (such as something higher in priority). Plus, when you executed it on your UI thread, it had your UI thread's priority: what priority are you setting the background-thread to? As DeveloperGuo suggests - you should probably instantiate the COM object on that background thread: if that object doesn't have to hang around, then it is generally cleaner and more efficient to make that thread have full responsibility for the COM objects and other resources that it uses, and just provide a higher-level abstract API for the rest of your program-code to use. These are just generic suggestions - can't get more specific without seeing code.

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