A Queued BackgroundWorker Using Generic Delegates
EDIT: Oops, sorry for the typo in the title. I meant “generic delegates”, not “anonymous delegates”.
Last week I wrote a post about using a generic wrapper around the BackgroundWorker class to execute long-running tasks on a different thread. This makes for a more responsive UI since the UI thread is free to process the Windows message pump.
The cool thing about using the BackgroundWorker (and also the BackgroundWorkerHelper) is that no matter how many times the user presses the button to start a long-running process, a new worker thread is created, executed, and marshalled back to the UI thread. This means that if you press a button that, say, runs a 5-second process at t=0, 1, 2, and 3 seconds, then you’ll see a result at t=5, 6, 7, and 8 seconds.
Most of the time this is the desired response. Recently I just ran into a particular scenario where I didn’t want the second long-running process to start until the first finished. In my case, I had a button that the user could press to print the current page. If they pressed the button again before the page had finished printing, then an error would be thrown. What I really need is a BackgroundWorker that queues up each new call to it and executes the next call only when the previous call has completed.
We can combine the same anonymous wrapper approach with a Queue to achieve this exact functionality! Here’s the code for the QueuedBackgroundWorker:
public static class QueuedBackgroundWorker
{
public static void QueueWorkItem(
Queue> queue,
Tin inputArgument,
Func, Tout> doWork,
Action> workerCompleted)
{
if (queue == null) throw new ArgumentNullException("queue");
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));
}
queue.Dequeue();
if (queue.Count > 0)
{
QueueItem nextItem = queue.Peek();
nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument);
}
};
queue.Enqueue(new QueueItem(bw, inputArgument));
if (queue.Count == 1)
{
QueueItem nextItem = queue.Peek();
nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument);
}
}
Along with these generic helper classes:
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; }
}
public class QueueItem
{
public QueueItem(BackgroundWorker backgroundWorker, Tin argument)
{
this.BackgroundWorker = backgroundWorker;
this.Argument = argument;
}
public Tin Argument { get; private set; }
public BackgroundWorker BackgroundWorker { get; private set; }
}
To test this, I created a quick-n-dirty WinForms application with a button and a textbox. When the user preses the button, a new item is added to the QueuedBackgroundWorker’s Queue. When the long-running (in this case 1 second) process completes, it appends a message to the textbox. For example:
private Queue> m_Queue = new Queue>();
private int m_Count = 0;
private void btnStart_Click(object sender, EventArgs e)
{
QueuedBackgroundWorker.QueueWorkItem(
m_Queue,
m_Count++,
args =>
{
string now = DateTime.Now.ToLongTimeString();
int count = args.Argument;
Thread.Sleep(1000);
string message = String.Format("Inside background thread: Time={0} Count=[{1}]", now, count);
return new { Count = count, Message = message };
},
args =>
{
string now = DateTime.Now.ToLongTimeString();
int count = args.Result.Count;
string message = args.Result.Message;
string line = String.Format("Received Result: Time={0} Count={1} Message=[{2}]{3}",
now, count, message, Environment.NewLine);
txtOutput.AppendText(line);
});
}
After testing it out, it behaves exactly as expected
I can now use this in my application to queue up things to print. Each item will only begin printing after the previous one completes. Here’s a screenshot:
Essentially what we’ve done here is create a collection (queue) of input objects for the DoWork method of the BackgroundWorker. The really cool part is that the generic delegate for this method is type-safe and can be used with anonymous types (as seen in the above example, just for fun).
I’m really digging all of these new .NET 3.0 features
Hope someone finds that useful!
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 28th, 2008 at 12:25 am
[...] A Queued BackgroundWorker Using Generic Delegates [...]
July 7th, 2008 at 3:59 pm
Very good example ! But I’m not yet very familiar with C#
syntax, specially with lambda expression.
Do you have a possibility to convert the code in VB.NET ?
Thank you in advance.
July 7th, 2008 at 4:04 pm
Thanks Flavio. Unfortunately you’re out of luck — lambda expressions are not supported in VB.NET. You can always just use a named method, but it’s not as clean or readable as C#.
July 7th, 2008 at 4:14 pm
Lambda expressions not supported ?
I have written code in VS2008 (VB.NET) with lambda expressions…
July 7th, 2008 at 4:24 pm
Ah, well if you’ve used them in VB.NET then my complete ignorance of VB is showing
Best of luck with that. I thought I remembered reading something awhile back about no (or only partial) lambda support in VB but I could be wrong.
September 4th, 2008 at 11:29 am
Yes, VB.NET does support Lambdas, but they are a crippled version that you cannot use with the BackgroundWorker component.
In VB.NET Lambdas you cannot use expressions that do not return a value (they must all be functions) and you cannot create multi-line lambdas.
December 8th, 2008 at 6:53 pm
first of all, thank you for posting this great background worker code. i’ll freely admit that i’m new to delegates and lambdas, and i’m not quite sure how to add a background worker progress report method to what you already have here. is it possible to add a progress reporter to this? if so, would you provide some pointers on how i could go about it? thanks again!
December 16th, 2008 at 3:32 pm
Hi,
There are no left brackets in the code…see sample below. Is it possible to download the file somewhere?
public static void QueueWorkItem(
Queue> queue,
Tin inputArgument,
Func, Tout> doWork,
Action> workerCompleted)
January 14th, 2009 at 11:17 am
HTML is eating some of the signs. For anyone looking at this page and wanting to grab the code: If you View..Source (or whatever works to see the HTML code off your browser), you can get the complete code segments off the website.
April 30th, 2009 at 11:59 am
Awesome piece of code! I haven’t played around with even the new .NET 3.0 features yet but it looks like there’s some great new additions.
Cheers!
May 15th, 2009 at 9:16 am
Hi Matt. a colleague of mine and I were discussing the code sample you posted, and we were both wondering about your reasoning behind the design of this solution. Why are you queing up individual background threads, instead of sending the data you want processed synchronously to one background thread? Wouldn’t queing up additional threads create a lot of unnecesary overhead?
Thanks!
-John
July 13th, 2009 at 10:05 am
Matt, thanks for providing this example.
This solution is exactly what I needed for the application I’m writing. Your code required reading MSDN on my part, because I didn’t understand lambda and parameter typing, but it forced me to learn.
Thanks.