A Type-safe BackgroundWorker Wrapper

Not too long ago I created a type-safe implementation of the ThreadPool.QueueUserWorkItem method that has proved quite useful to spawn off a long-running process to a new thread. This works great if you have a method that takes an input argument and doesn’t return anything. However, what if your long-running process needs to return a value or complete an action when it’s finished? The situation usually arises whenever you need to run a long process on another thread and update the UI when that process is done.

The tricky part about doing the UI update is that you must update it using only the thread that created it. This usually involves checking the InvokeRequired property of the control, and then calling Invoke with a delegate to a method that gets called on the correct UI thread.

Thankfully .NET 2.0 has a BackgroundWorker class which does all of this behind the scenes. A typical scenario where the BackgroundWorker really shines would be to give a long-running computation an input argument, let it run, and then receive an output result that updates the UI. For example:

        private void button6_Click(object sender, EventArgs e)
        {
            // Runs on the UI thread
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            
            string input = "Input argument";

            bw.RunWorkerAsync(input);
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            // Runs on different thread
            string input = (string)e.Argument;
            // Long-running computation
            e.Result = input.ToUpper();            
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Runs on the UI thread
            string result = (string)e.Result;
            txtOutput.AppendText(String.Format("Completed! Result={0}", result));
        }

This works great, though it has a few problems. First off, it is certainly not typesafe since the input argument to RunWorkerAsync is just an object. This means that in the DoWork event handler that e.Argument is also an object. Similarly, the e.Result property is an object, as well as the Result property in the RunWorkerCompleted event handler. We have objects flying around everywhere, and could easily write code that, for example, assumes that DoWork is returning a string when in fact it might be returning a DateTime. This could get ugly, not to mention that we can’t take advantage of anonymous types without type safety across these methods. This is where a generic wrapper for the BackgroundWorker really comes in handy:

So, for this situation we’re assuming that we have an input argument of type Tin that gets passed to the DoWork event handler on a different thread. That does an operation on the input argument and creates a return value of type Tout, and returns it (along with an Exception, if there was one). So we can write two generic helper classes to handle these types:

    public class DoWorkArgument
    {
        public DoWorkArgument(T argument)
        {
            this.Argument = argument;
        }

        public T Argument { get; private set; }
    }

    public class WorkerResult
    {
        public WorkerResult(T result, Exception error)
        {
            this.Result = result;
            this.Error = error;
        }

        public T Result { get; private set; }
        public Exception Error { get; private set; }
    }

This is fairly straightforward, and we’ve used the new “automatic properties” feature of C# 3.0 to make our code more readable.

Next we take a look at the BackgroundWorker implementation. I put everything in a static class named BackgroundWorkerHelper that contains a few static helper methods. The first method is:

    public static class BackgroundWorkerHelper
    {
        public static void DoWork(
            Tin inputArgument,
            Func, Tout> doWork,
            Action> workerCompleted)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = false;
            bw.DoWork += (sender, args) =>
            {
                if (doWork != null)
                {
                    args.Result = doWork(new DoWorkArgument((Tin)args.Argument));
                }
            };
            bw.RunWorkerCompleted += (sender, args) =>
            {
                if (workerCompleted != null)
                {
                    workerCompleted(new WorkerResult((Tout)args.Result, args.Error));
                }
            };
            bw.RunWorkerAsync(inputArgument);
        }

This defines a method called DoWork which takes three input arguments. The first argument is the input argument and is of type Tin. The second argument is a generic delegate, specifically a Func, Tout> delegate. This means that a delegate to a method must be given which takes one input parameter, a DoWorkArgument object, and returns an object of type Tout. The third input argument is another generic delegate which must have a method specified that accepts one input object of type WorkerResult and does not return anything.

The rest of the implementation creates a BackgroundWorker and hooks up to its DoWork and RunWorkerCompleted events. Note that we’ve made the simplification that we won’t be supporting cancellation or reporting of progress. A more sophisticated wrapper could be implemented that takes advantage of these features if they are needed. I chose to stick with this for simplicity.

The DoWork event has code attached to it through the use of a lambda expression which attempts to invoke the doWork delegate that was passed in (if it’s not null). The args.Argument object is cast to type Tin before a new DoWorkArgument is passed to the delegate invocation, and its result assigned to args.Result. A similar approach is taken on the RunWorkerCompleted event, though no return value is specified.

We can use this BackgroundWorkerHelper to reduce our example above to:

       private void button7_Click(object sender, EventArgs e)
        {
            string input = "Input argument";
            BackgroundWorkerHelper.DoWork(
                input,
                (args) =>
                {
                    // Runs on different thread
                    string input = args.Argument;
                    // Long-running computation
                    return input.ToUpper();
                },
                (args) =>
                {
                    // Runs on the UI thread
                    string result = args.Result;
                    txtOutput.AppendText(String.Format("Completed! Result={0}", result));
                });
        }

Notice that the C# 3.0 compiler can infer the types on everything (in this case Tin=string and Tout=string) and requires no casting. It’s also much less code, and really gets the point across that an input is given to the long-running operation, which then calls the last piece of code to update the UI. It flows naturally from top to bottom without jumping around between methods.

This covers the situation where you have a long-running method that takes an input and returns an output, but I’ve run into two other similar situations where this can be simplified. For example, if you have a long-running method that doesn’t take any input parameters, but needs to update the UI when it’s done. In this case, we can simplify the DoWork method to:

        public static void DoWork(
            Func doWork,
            Action> workerCompleted)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = false;
            bw.DoWork += (sender, args) =>
            {
                if (doWork != null)
                {
                    args.Result = doWork();
                }
            };
            bw.RunWorkerCompleted += (sender, args) =>
            {
                if (workerCompleted != null)
                {
                    workerCompleted(new WorkerResult((Tout)args.Result, args.Error));
                }
            };
            bw.RunWorkerAsync();
        }

Similarly, if you encounter a situation where the long-running method takes no input and returns no output, but it would be useful to update the UI (or check for an error, etc) when the method is complete, it can be further simplified to:

        public delegate void Func();

        public static void DoWork(
            Func doWork,
            Action workerCompleted)
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = false;
            bw.DoWork += (sender, args) =>
            {
                if (doWork != null)
                {
                    doWork();
                }
            };
            bw.RunWorkerCompleted += (sender, args) =>
            {
                if (workerCompleted != null)
                {
                    workerCompleted(args.Error);
                }
            };
            bw.RunWorkerAsync();
        }

Hope somebody finds that useful! It’s helped me write the tricky multi-threaded code that usually results when trying to create a responsive user interface by spawning long-running processes out to the thread pool while still retaining thread affinity for the UI controls.

If there’s enough interest I can make the BackgroundWorkerHelper.cs file available for download so you don’t need to copy/paste everything in this post. :)

EDIT: I also created a “Queued BackgroundWorker” that only starts the next long-running process after the first one finishes.


You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

AddThis Social Bookmark Button

18 Responses to “A Type-safe BackgroundWorker Wrapper”

  1. Yes, functional programming excels at user interfaces. It’s also great for showing blocking UI while perfoming actions. A lot of our UI code has stuff like this:

    ProgressDialog.ShowAction(pd => {
    // long running code
    pd.Close();
    });

    This ends up getting nested in a few layers of async stuff, dispatching, error handling and so on. But it’s so much easier than the alternatives.

  2. […] A Type-safe BackgroundWorker Wrapper […]

  3. […] A Type-safe BackgroundWorker Wrapper […]

  4. […] A common scenario usually encountered when creating responsive user interfaces (e.g. WinForms applications) is to let a long-running piece of code execute on a background thread and then update the user interface when it completes.  The catch is that the user interface update at the end must be completed on the UI thread.  Thankfully .NET Framework shipped with a BackgroundWorker created explicitly for this task which handles the marshalling between background thread and UI thread automatically.   It even supports cancellation and can report progress back to the UI thread in addition to notifying the UI thread that it has completed.  This is also discussed in the "Threading in C#" book mentioned above.  In addition, only slight modifications to the BackgroundWorker are needed to use anonymous types as well. […]

  5. […] Linkshttp://thevalerios.net/matt/2008/05/a-type-safe-backgroundworker-wrapper/ http://weblogs.asp.net/justin_rogers/articles/126345.aspx […]

  6. Do you mind making a BackgroundWorkerHelper.cs file I can’t seem to get this to work. Too many squigley red lines so it won’t compile.

  7. Your web page is mangling the code. To Bob and others: view the page source to see the correct code.

  8. Thanks, Matt – this is really helpful. Would you mind posting (or emailing) the CS file. The code is being mangled by the web page.

  9. public static class BackgroundWorkerHelper {

    ///
    /// Creates a BackgroundWorker and hooks up to its DoWork and RunWorkerCompleted events.
    /// This does not support cancellation or reporting of progress.
    ///
    ///
    ///
    /// input argument of type Tin
    /// generic delegate which takes one
    /// input parameter, a DoWorkArgument object, and returns an object of type Tout.
    /// generic delegate which must have a method
    /// specified that accepts one input object of type WorkerResult and does not return anything.
    public static void DoWork(
    Tin inputArgument,
    Func<DoWorkArgument, Tout> doWork,
    Action<WorkerResult> workerCompleted) {
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = false;
    bw.WorkerSupportsCancellation = false;
    bw.DoWork += (sender, args) => {
    if (doWork != null) {
    args.Result = doWork(new DoWorkArgument((Tin)args.Argument));
    }
    };
    bw.RunWorkerCompleted += (sender, args) => {
    if (workerCompleted != null) {
    workerCompleted(new WorkerResult((Tout)args.Result, args.Error));
    }
    };
    bw.RunWorkerAsync(inputArgument);
    }

    ///
    /// Similar to the above, except, use if one has a long-running method that
    /// doesn’t take any input parameters, but needs to update the UI when it is done.
    ///
    ///
    /// generic delegate which takes no
    /// input parameter, but returns an object of type Tout.
    /// generic delegate which must have a method
    /// specified that accepts one input object of type WorkerResult and does not return anything.
    public static void DoWork(
    Func doWork,
    Action<WorkerResult> workerCompleted) {
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = false;
    bw.WorkerSupportsCancellation = false;
    bw.DoWork += (sender, args) => {
    if (doWork != null) {
    args.Result = doWork();
    }
    };
    bw.RunWorkerCompleted += (sender, args) => {
    if (workerCompleted != null) {
    workerCompleted(new WorkerResult((Tout)args.Result, args.Error));
    }
    };
    bw.RunWorkerAsync();
    }

    public delegate void Func();

    ///
    /// Similar to the above, except, use if one has a long-running method that
    /// doesn’t take any input parameters, does not return output, but may need
    /// to update the UI when it is done.
    ///
    /// generic delegate which takes no
    /// input parameter, and returns no output.
    ///
    public static void DoWork(
    Func doWork,
    Action workerCompleted) {
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = false;
    bw.WorkerSupportsCancellation = false;
    bw.DoWork += (sender, args) => {
    if (doWork != null) {
    doWork();
    }
    };
    bw.RunWorkerCompleted += (sender, args) => {
    if (workerCompleted != null) {
    workerCompleted(args.Error);
    }
    };
    bw.RunWorkerAsync();
    }
    }

    ///
    /// input argument of type Tin that gets passed to the DoWork event handler
    /// on a different thread.
    ///
    ///
    public class DoWorkArgument {
    public T Argument { get; private set; }

    public DoWorkArgument(T argument) {
    this.Argument = argument;
    }
    }

    ///
    /// return value of type Tout
    ///
    ///
    public class WorkerResult {
    public T Result { get; private set; }
    public Exception Error { get; private set; }

    public WorkerResult(T result, Exception error) {
    this.Result = result;
    this.Error = error;
    }
    }
    }

  10. Cool component!, thanks for that. Have anybody implemented a version that supports cancel and progress changed?

  11. I liked your cless so much I extended it.
    Thx for the inspiration. Cheers, Ivan

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    using System.ComponentModel;
    using System.Threading;
    using System.Collections.Generic;
    using System.Text;

    namespace DB
    {
    public class DoWorkArg
    {
    public T Arg { get; private set; }
    public Action ReportProgress;
    public Action CancelAsync;

    public DoWorkArg(T Arg, Action reportProgress, Action cancelAsync)
    {
    this.Arg = Arg;
    this.ReportProgress = reportProgress;
    this.CancelAsync = cancelAsync;
    }
    }

    public class WorkerResult
    {
    public T Result { get; private set; }
    public Exception Error { get; private set; }
    public bool Cancelled { get; private set; }

    public WorkerResult(T result, Exception error, bool cancelled)
    {
    this.Result = result;
    this.Error = error;
    this.Cancelled = cancelled;
    }
    }

    public class WorkerProgress
    {
    public int ProgressPercentage { get; private set; }
    public Tus UserState { get; private set; }

    public WorkerProgress(int progressPercentage, Tus userState)
    {
    this.ProgressPercentage = progressPercentage;
    this.UserState = userState;
    }
    }

    public class QueueItem
    {
    public Tin Arg { get; private set; }
    public BackgroundWorker BackgroundWorker { get; private set; }

    public QueueItem(BackgroundWorker backgroundWorker, Tin Arg)
    {
    this.BackgroundWorker = backgroundWorker;
    this.Arg = Arg;
    }
    }

    //public static class QueuedBackgroundWorker
    //{
    // public static void QueueWorkItem(
    // Queue<QueueItem> queue,
    // Tin inputArg,
    // Func<DoWorkArg, Tout> doWork,
    // Action<WorkerResult> workerCompleted)
    // {
    // if (queue == null)
    // {
    // throw new ArgumentNullException(“queue”);
    // }
    // BackgroundWorker bw = new BackgroundWorker();

    // bw.WorkerReportsProgress = true;
    // bw.WorkerSupportsCancellation = true;
    // bw.DoWork += (sender, args) =>
    // {
    // if (doWork != null)
    // {
    // args.Result = doWork(new DoWorkArg((Tin)args.Argument));
    // }
    // };

    // bw.RunWorkerCompleted += (sender, args) =>
    // {
    // if (workerCompleted != null)
    // {
    // workerCompleted(new WorkerResult((Tout)args.Result, args.Error));
    // }
    // queue.Dequeue();
    // if (queue.Count > 0)
    // {
    // QueueItem nextItem = queue.Peek();
    // nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Arg);
    // }
    // };

    // queue.Enqueue(new QueueItem(bw, inputArg));
    // if (queue.Count == 1)
    // {
    // QueueItem nextItem = queue.Peek();
    // nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Arg);
    // }
    // }
    //}

    public static class BackgroundWorkerHelper
    {
    public static void DoWork(
    Tin inArg,
    Func<DoWorkArg, Tout> doWork,
    Action<WorkerResult> workerCompleted,
    Action<WorkerProgress> progressChanged)
    {
    BackgroundWorker bw = new BackgroundWorker();

    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;
    bw.DoWork += (sender, args) =>
    {
    if (doWork != null)
    {
    Action reportProgress = new Action(bw.ReportProgress);
    Action cancelAsync = new Action(bw.CancelAsync);
    args.Result = doWork(new DoWorkArg((Tin)args.Argument, reportProgress, cancelAsync));
    }
    };
    bw.RunWorkerCompleted += (sender, args) =>
    {
    if (workerCompleted != null)
    {
    workerCompleted(new WorkerResult((Tout)args.Result, args.Error, args.Cancelled));
    }
    };
    bw.ProgressChanged += (sender, args) =>
    {
    if (progressChanged != null)
    {
    progressChanged(new WorkerProgress(args.ProgressPercentage, (Tus)args.UserState));
    }
    };
    bw.RunWorkerAsync(inArg);
    }
    }
    }

  12. hello,

    can you post the source code for this example?

    Thank you

  13. A very good starting point, of course, if you use it in a Windows Form application it requires a deep knowledge of how the InvokeRequired works for the UserControl.
    In WPF this is not needed anymore.
    Thanks

  14. public static void DoWork1(object input, Func doWork, Action workerCompleted)
    {
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = false;
    bw.WorkerSupportsCancellation = false;
    bw.DoWork += (sender, args) =>
    {
    if (doWork != null)
    {
    args.Result = doWork(input);
    }
    };
    bw.RunWorkerCompleted += (sender, args) =>
    {
    if (workerCompleted != null)
    {
    workerCompleted(args.Result);
    }
    };
    bw.RunWorkerAsync(input);
    }

    private void button1_Click(object sender, EventArgs e)
    {
    BackgroundWorkerHelper.DoWork1(“test value”,
    (input) =>
    {
    System.Threading.Thread.Sleep(2000);
    return input + ” 1″;
    },
    (res) =>
    {
    label1.Text = (string)res;
    }
    );
    }

  15. any zip with all code ?? it will be useful, code and samples thx

  16. samples in comments and post not compiles !!! thx

  17. Hello, a great article. Thanks a lot for your help. I incorporated the code in a project and enhanced the classes a little, to have progress reporting and cancel posibilities… Sample code is shared for free here:

    http://blogs.mahop.net/Markus/Entry/A_Type_Save_Backgroundworker_in_C_.aspx

    Nice greetings from Germany,
    Markus

  18. You never dispose that wrapper. It’s a memory leak!

Leave a Reply