Alternative to asynchronous programming patterns for WinForm apps in .NET 3.5 and 4.0

Do you know that situation? You have a WinForm application and you want to do some long running operation and you don't want to block the GUI in the meantime...

From the beginning of the .NET era three patterns for asynchronous programming were developed. All are well described at MSDN: Asynchronous Programming Patterns. In short they are:

To be honest, each of them has own disadvantages. Which they are I'll explain in the future (maybe). But two are shared in common:

  1. If you want to update GUI controls during asynchronous processing you have to invoke those updates into GUI thread. Ok, there are "clever" components such as Rebex which use the callers synchronization context (if enabled) to raise asynchronous events (so events run in the correct thread automatically if the operation was started from the GUI thread), but generally you have to use the Control.InvokeRequired property and the Control.Invoke() method to do the work.
  2. If you want to do something more complicated than executing one async operation and display "Finished" when the operation is finished, you may ended with total spaghetti code difficult to read or debug. Of course it's not necessary, but I have seen such code (and yes, it was also my code in my beginnings :-).

How to solve these problems?

  1. If it is possible, use .NET 4.5 and Async/Await pattern (it may solve at least the second problem).
  2. If you are not so lucky the suggestion is: don't use the patterns above, just simply write your code synchronously, but execute it whole in a background thread.

Finally, to the core of this article: how to write synchronous code which runs in background and updates the GUI controls in a nice way?

Use the LINQ and behold:

private void button1_Click(object sender, EventArgs e)
{
	// run the method in a background thread
	RunInBackground(DoSomething);
}

/// <summary>
/// Do some work. This can be run synchronously or asynchronously without problems.
/// </summary>
private void DoSomething()
{
	// updates GUI safely, but note that this call will block GUI
	InvokeIfNeeded(() => label1.Text = "Running...");

	// do something
	Thread.Sleep(1000);

	// updates GUI safely
	InvokeIfNeeded(() =>
	{
    label1.Text = "In the middle of processing...";
    label1.Text = "I am working hard!";
	});

	// do something else
	Thread.Sleep(1000);

	// updates GUI safely
	InvokeIfNeeded(() => label1.Text = "Finished");
}

private void button2_Click(object sender, EventArgs e)
{
	// do something
	label1.Text = "Starting";

	// start processing of the following code in a background thread
	// note that the RunInBackground method returns "immediately"
	// it doesn't execute the code, it only starts a background thread
	// execution of the code is scheduled for future
	RunInBackground(() =>
	{
    // do something
    Thread.Sleep(1000);

    // updates GUI safely
    InvokeIfNeeded(() => label1.Text = "Finished");
	});

	// do something else, but note that this call is executed
	// before the code above is even started (almost sure)
	// more over this call will block GUI again
	Thread.Sleep(1000);
}

The same in VB.NET:

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    ' run the method in a background thread
    RunInBackground(AddressOf DoSomething)
End Sub

''' <summary>
''' Do some work. This can be run synchronously or asynchronously without problems.
''' </summary>
Private Sub DoSomething()
    ' updates GUI safely, but note that this call will block GUI
    InvokeIfNeeded(Sub() Label1.Text = "Running...")

    ' do something
    Thread.Sleep(1000)

    ' updates GUI safely
    InvokeIfNeeded(
    Sub()
        Label1.Text = "In the middle of processing..."
        Label1.Text = "I am working hard!"
    End Sub)

    ' do something else
    Thread.Sleep(1000)

    ' updates GUI safely
    InvokeIfNeeded(Sub() Label1.Text = "Finished")
End Sub

Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    ' do something
    Label1.Text = "Starting"

    ' start processing of the following code in a background thread
    ' note that the RunInBackground method returns "immediately"
    ' it doesn't execute the code, it only starts a background thread
    ' execution of the code is scheduled for future
    RunInBackground(
    Sub()
        ' do something
        Thread.Sleep(1000)

        ' updates GUI safely
        InvokeIfNeeded(Sub() Label1.Text = "Finished")
    End Sub)
    
    ' do something else, but note that this call is executed
    ' before the code above is even started (almost sure)
    ' more over this call will block GUI again
    Thread.Sleep(1000)
End Sub

Isn't it nice? The only thing left is to define the RunInBackground and InvokeIfNeeded methods. Here they are:

using System.ComponentModel;

...

/// <summary>
/// Invokes specified action if required. Executed action will block GUI.
/// </summary>
/// <param name="action">An action to invoke.</param>
private void InvokeIfNeeded(Action action)
{
    if (InvokeRequired)
        Invoke(action);
    else
        action();
}

/// <summary>
/// Starts the action in a background thread.
/// </summary>
/// <param name="action">An action to invoke.</param>
private void RunInBackground(Action action)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += (s, e) => action();
    worker.RunWorkerCompleted += (s, e) =>
    {
        // process unhandled exceptions
        if (e.Error != null)
        {
            InvokeIfNeeded(() =>
            {
                // show error in dialog box
                MessageBox.Show(string.Format(
                    "An exception occured in a background thread.\n{0}", e.Error));
            });
        }
    };
    worker.RunWorkerAsync();
}

Also for VB.NET:

Imports System.ComponentModel

...

''' <summary>
''' Invokes specified action if required.
''' </summary>
''' <param name="action">An action to invoke.</param>
Private Sub InvokeIfNeeded(ByVal action As Action)
    If InvokeRequired Then
        Invoke(action)
    Else
        action()
    End If
End Sub

''' <summary>
''' Starts the action in a background thread.
''' </summary>
''' <param name="action">An action to run.</param>
Private Sub RunInBackground(ByVal action As Action)
    Dim worker = New BackgroundWorker()
    AddHandler worker.DoWork, Sub() action()
    AddHandler worker.RunWorkerCompleted,
        Sub(s As Object, e As RunWorkerCompletedEventArgs)
            ' process unhandled exceptions
            If e.Error IsNot Nothing Then
                InvokeIfNeeded(
                Sub()
                    ' show error in dialog box
                    MessageBox.Show(String.Format(
                        "An exception occured in a background thread.{0}{1}", vbNewLine, e.Error))
                End Sub)
            End If
        End Sub
    worker.RunWorkerAsync()
End Sub

If you don't want to have the InvokeIfNeeded and RunInBackground methods on each WinForm, you can add System.Windows.Forms.Control to the parameters and do it like this:

private void InvokeIfNeeded(Control control, Action action)
{
    if (control.InvokeRequired)
        control.Invoke(action);
    else
        action();
}

...

InvokeIfNeeded(this, () => label1.Text = "Running...");

And VB.NET:

Private Sub InvokeIfNeeded(ByVal control As Control, ByVal action As Action)
    If control.InvokeRequired Then
        control.Invoke(action)
    Else
        action()
    End If
End Sub

...

InvokeIfNeeded(Me, Sub() Label1.Text = "Running...")

Any comments are welcome (you can send them to lukas.matyska@rebex.net).