Using BackgroundWorker Effectively
By using a pattern of method calls (RunWorkerAsync, ReportProgress) and event callbacks (DoWork, ProgressChanged, RunWorkerCompleted), BackgroundWorker helps you easily create a background thread and factor out the stuff that should be running on the background thread versus the foreground thread. If you’re not familiar with this component – Erick created a great slide in his presentation showing how this all glues together.
If you attended his PDC talk, or have viewed it here (or downloaded it here), there is one important subject that should be mentioned when talking about the BackgroundWorker: While it is easy to now create multithreaded applications, the decision to make an application multithreaded should not be taken lightly.
Lets take an analogy from the real world. The holiday season is coming up, and I’m so busy coding up a storm I really don’t feel like shopping. The question is, should I stop what I am doing to start surfing the web to mail order stuff, or should I hire more resources to do my shopping for me? Well right about now, a PersonalShopper sounds great.
So I hire the personal shopper to go off and buy some items for me at the mall. (If you’re following along, this is like dropping on the BackgroundWorker to create a second thread.) Originally, I’m not quite so sure how long the PersonalShopper is going to take, so I give them a list of one item to fetch at a time. Well this PersonalShopper is so awesome at doing what they do, they’re back in a flash, and all of the sudden, instead of being able to get my coding done, I’m spending all my time chatting with PersonalShopper about the next thing to buy. Essentially PersonalShopper is so chatty with me, it’s as if my coding time is suffering a mini-denial-of-service attack. =)
I think, wait a minute, hmm. I’ll get more time if I send PersonalShopper out to buy large quantities of presents at once. So I think about a list (take a little while to compile everything – didn’t I get Erick that red scarf last year?), and send PersonalShopper out on a spree. The only problem is PersonalShopper seems to always have the car at the mall – going out in spurts of three hours at a time. Normally the car is available to me most of the time; however it seems that lately I have trouble getting the car to drive to work myself.
Finally PersonalShopper shows back up at my place with all of the presents I asked for – but now it takes so long to unload the car, that I can’t get any coding done.
So let’s review the contentions that anyone building a multithreaded application has to manage:
- Reporting too many results too quickly can starve the UI thread from being able to process its own work.
- The additional mechanics of resource sharing (CPU, memory, etc) adds additional complexity to the application, which if not carefully managed can bog down the entire app.
- Reporting too many results at once can choke the UI thread with updates to the UI.
I can’t really give guidance beyond the things you have to look out for, as each application has its own performance characteristics. Windows Forms 2.0 has made it really easy to go out and hire that PersonalShopper, but it is up to the developer to figure out if background task can be efficiently managed without dragging down the entire application.