InvokeRequired / BeginInvoke Method
To ensure a method is thread safe for windows controls/forms, it is common to implement the InvokeRequired/BeginInvoke pattern.
// No Parameter Example void doSomething() { if (this.InvokeRequired) { this.BeginInvoke(new Action(doSomething)); return; } // Insert code that does work here } // Function with Parameter example. void doSomething(int param1, string param2) { if (this.InvokeRequired) { this.BeginInvoke( new Action<int, string>(doSomething), new object[] { param1, param2}); return; } // Insert code that does work here }
Functionally, this works fine. The question is can we make it better? I believe the answer is yes.
Currently there are two main problems with this approach:
- For a parametrized function, when updated, the corresponding delegate signature must also be updated.
- Too often, I’ve seen the return statement missing within the if block leading to errors that are not found until run-time
The aim is to define a software architecture that will simplify the code, and improve code correctness.
Proposed Solution
A GUIExtensions static class has been created containing two static functions that allow us to execute the code on the thread that the control/form was created either synchronously or asynchronously.
using System.ComponentModel; public static class GUIExtensions { /// <summary> /// Executes the delegate immediately if the current thread is the thread /// that created this object. Otherwise, asynchronously executes the delegate /// on the thread that created this object. /// </summary> /// <param="control">The control of interest</param> /// <param="code">The code to execute on the control's creation thread</param> /// <remarks> /// If you need to execute the code immediately, use the <code>UIThreadInvoke</code> /// method instead. /// </remarks> static public void UIThread(this ISynchronizeInvoke control, Action code) { // Determine whether the current thread is the creation thread of the control. if (control.InvokeRequired) { // Asynchronously execute the delegate on the thread that created this object. control.BeginInvoke(code, null); return; } // Just invoke the code. code.Invoke(); } /// <summary> /// Executes the delegate immediately if the current thread is the thread /// that created this object. Otherwise, synchronously executes the delegate /// on the thread that created this object and marshals the call to the creating thread. /// </summary> /// <param name="control">The control of interest.</param> /// <param name="code">The code to execute on the control's creation thread.</param> static public void UIThreadInvoke(this ISynchronizeInvoke control, Action code) { // Determine whether the current thread is the creation thread of the control. if (control.InvokeRequired) { // Synchronously execute the delegate on the thread that created // this object and marshals the call to the creating thread. control.Invoke(code, null); return; } // Just invoke the code. code.Invoke(); } }
Example Usage
The best way to see the power of this proposed solution is to have a look at it in action.
The UIThread static functions may be called with anonymous functions (as shown below). Note that there are a couple of significant advantages of using this approach:
- If the doSomething() signature changes, you do NOT have to update a inner-code delegate signature
- You can use the function parameters directly
void doSomething(int param1, string param2) { this.UIThread(delegate { // Insert code that does work here // NOTE: you can use param1, param2 directly here. }); }
The UIThread static functions may also be called with lambda functions. This allows you to do quick in-line updates of GUI components within background threads.
void doSomething(int param1, string param2) { // Insert code here that's doing something // Now update a GUI Text box on the correct thread this.UIThread(() => this.myTextBox.Text = "Some Text";); // Insert code here that's doing something }