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
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
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.
May 16th, 2008 at 3:12 pm
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.
May 19th, 2008 at 4:56 pm
[...] A Type-safe BackgroundWorker Wrapper [...]
May 19th, 2008 at 8:49 pm
[...] A Type-safe BackgroundWorker Wrapper [...]
June 14th, 2008 at 3:55 pm
[...] 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. [...]
June 24th, 2008 at 11:04 pm
[...] Linkshttp://thevalerios.net/matt/2008/05/a-type-safe-backgroundworker-wrapper/ http://weblogs.asp.net/justin_rogers/articles/126345.aspx [...]
January 23rd, 2009 at 9:49 pm
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.
May 28th, 2009 at 4:11 pm
Your web page is mangling the code. To Bob and others: view the page source to see the correct code.
June 11th, 2009 at 5:52 pm
Thanks, Matt – this is really helpful. Would you mind posting (or emailing) the CS file. The code is being mangled by the web page.
June 11th, 2009 at 7:55 pm
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;
}
}
}
September 7th, 2009 at 9:59 am
Cool component!, thanks for that. Have anybody implemented a version that supports cancel and progress changed?
September 30th, 2009 at 3:20 am
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);
}
}
}
January 13th, 2010 at 12:31 pm
hello,
can you post the source code for this example?
Thank you
April 28th, 2010 at 8:28 am
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