Winform: multiple threads Updating the user interface at the same time

I inherited some code with two non-UI threads that update various WinForm controls.
The code uses InvokeRequired and Invoke to update the user interface; however, I still get an error from time to time: Invalid cross-thread operation: Control 'lvReports' accessed by a thread other than the one on which it was created.

I suspect I'm dealing with race conditions and that I need to enter a lock in the method below, but I said that I can find dozens of examples of how to safely update the user interface from a thread other than the UI, but without examples or a discussion of how to deal with two threads updating the same controls in a race script.

So my question is: how can I rewrite the code below to handle the user interface correctly, given the state of the race, and what do I need to update the interface from threads other than the UI?

// two separate theads call this method in a instance of a WinForm private void LoadReports() { if (this.InvokeRequired) { this.Invoke(new MethodInvoker(this.LoadReports)); } else { // some code removed to keep exampe simple... SetCtlVisible(lvReports, true); if (this.InvokeRequired) { this.Invoke((MethodInvoker)delegate { lvReports.Refresh(); }); } else { lvReports.Refresh(); } } } delegate void SetVisibleCallback(Control ctl, bool visible); private void SetCtlVisible(Control ctl, bool visible) { if (ctl.InvokeRequired) { SetVisibleCallback d = new SetVisibleCallback(SetCtlVisible); ctl.Invoke(d, new object[] { ctl, visible }); } else { ctl.Visible = visible; } } 

Here are some thoughts: Does this.InvokeRequired differ from ctl.InvokeRequired anytime? Is a second InvokeRequired test required given the first? Is an implementation of SetCtlVisible required if I save the first InvokeRequired? Should I remove the first InvokeRequired and save all the code in the else clause? Is locking required around an else clause?

+4
source share
1 answer

Using InvokeRequired like this is an anti-pattern. You know that this method is called from a thread, InvokeRequired should always be true.

Now you can use it for troubleshooting. If this is a lie, then something is seriously wrong. Throw an exception, the debugger will stop and let you know why it is not working properly. And always call Invoke (), it is called to a small helper method that does the rest of the LoadReports () load.

Also note that you are using it incorrectly in the rest of your code. You know that the rest of LoadReports () works in the UI thread, you used Invoke (). It makes no sense to test it again, including inside SetCtlVisible ().

A typical reason for getting a bomb is because the thread runs LoadReports () too quickly before the form window is created. You need to block it. The Form Load event is a signal.

+9
source

Source: https://habr.com/ru/post/1333862/


All Articles