Making Thread-Safe Calls on Windows Controls/Forms

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  
}

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>