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?
source share