BackgroundWorkers are pretty exciting, because they are one of the easiest classes to use, when it comes to running processor intensive operations on a separate dedicated thread.
Many programmers that wouldn’t even begin to think about getting into multithreading, because of it’s complexities, can actually feel comfortable with the BackgroundWorker, and quickly begin to incorporate multithreading into their applications.
Because the BackgroundWorker performs its operations on a separate dedicated thread, it prevents your application from freezing up, and becoming non-responsive while it waits for the operations to be completed.
To begin with, let’s create a scenario that simulates intensive processing by calling the System.Threading.Thread.Sleep method, which will block the main thread, causing the application to freeze for 5 seconds.
Create a new Visual Basic Windows Forms Application project, and name it WorkerDemo. As soon as the project has been created, add a new TextBox to the form, and a new Button to the form (leave the button named Button1). Mine looks like this:
Now, double-click the button to create the Button1_Click event handler, and insert a line of code calling the Sleep method, as follows:
Next, run the program (press F5). Then press the button, and try to enter text into the TextBox. Can you do it? Try moving the Form around. Can you do it?
If everything worked like it’s supposed to, your application should have froze, and you shouldn’t have been able to do anything for 5 seconds after clicking the button.
Freezing, obviously, is not the type of behavior you expect from a program. So let’s look at how we can use the BackgroundWorker to perform our simulated intensive processing operation on a separate dedicated thread.
But before we do, remove the line of code that we added to the Button1_Click event handler.
To start working with the BackgroundWorker class, we need to create a new instance of it at the member (class) level, using the “WithEvents” keyword. The “WithEvents” keyword causes the instance of the BackgroundWorker to be listed in the “Class Name” drop-down list at the top of the code window, and it’s methods to be listed in the “Method Name” drop-down list.
Using the “WithEvents” keyword is not required to declare and work with the BackgroundWorker, but we will use it for this example.
Before we make our declaration, add an Imports statement for the System.ComponentModel namespace above the Form1 class.
After importing the ComponentModel namespace, add the declaration for the BackgroundWorker at the top of the Form1 class.
Our code should now look like this:
Now that we have declared our BackgroundWorker, go to the “Class Name” drop-down list at the top of the code window and select “mWorker”. Then, select “DoWork” from the “Method Name” drop-down list. This will create the mWorker_DoWork event handler. Here’s an example:
If you performed the above step correctly, you should have a new method named “mWorker_DoWork”. This method will be executed when the BackgroundWorker’s RunWorkerAsync() method is called. And, it will execute on a separate dedicated thread.
So, the next thing we need to do is add some code to simulate some intensive processing. Let’s use the same Sleep method that we used above, because we know that caused our form to freeze for 5 seconds last time.
And here you have it:
Alright, the last thing we need to do is add the code that is necessary to get the BackgroundWorker running. Since the BackgroundWorker’s RunWorkerAsync() is the method that starts the processing on a separate thread, we’ll insert that. If you still have the Button1_Click method in your class, then add it there as follows:
Now that we have the BackgroundWorker all wired up, let’s test it out! Start your program (F5)…
Can you enter any text into the TextBox? Can you move the form around? Absolutely you can!
Congratulations! You just learned how to implement multithreading into your application!
Working with Arguments…
Ok… It’s really cool to simulate some processing on a separate thread! However, what we really need is a real-world example. Because real-world applications do more meaningful things, like performing downloads and databases transactions.
For this real-world example, let’s retrieve a bunch of data from a database, and load it into a DataGridView for display.
You’re welcome to follow along with me, if you want. I am going to use the SQL Server AdventureWorks database. SQL Server Express or SQL Server 2005 is required to run it. Here’s a link to an MSDN walkthrough:
If you’re going to use a different database, then you’ll just have to make sure you have the connection string, the correct provider (OleDb, Odbc, etc.), the query syntax, and enough information to retrieve.
For this example, we will retrieve all of the names in the Contact table of the AdventureWorks database. There are over 19,000 names in that table, so that should take a little bit of time to retrieve.
Since you already have your form set up, we’ll use that.
This time we’ll start with the Button1_Click method: Create a string variable to store you SELECT statement in. Then, pass the string variable as an argument of the RunWorkerAsync() method.
The RunWorkerAsync() method will actually start the BackgroundWorker on a separate thread, passing the SQL string as an argument.
The next thing we need to do is go to the mWorker_DoWork method, and insert code to retrieve the data from the database. Remember that this method will run on a separate thread.
The following steps needs to be taken:
- Retrieve the SQL string that was passed
- Create the connection string
- Create a table to store the data
- Create and configure a DataAdapter
- Fill the table using the DataAdapter
- Return the filled table to the main thread
Here’s our example that accomplishes all of the above:
Important Note! The mWorker_DoWork method is being executed on a separate thread. Therefore, it cannot directly access controls on the form, such as our DataGridView. Attempting to do so will result in a cross-threading violation. So instead of filling the DataGridView from within this method, we must pass the filled table back to the main thread using the e.Result property.
The final thing we need to do is add the RunWorkerCompleted event handler to our code window. This method is fired after the BackgroundWorker has completed it’s work. Therefore, we can use this method to retrieve the filled table from the BackgroundWorker and set it as the DataSource of our DataGridView.
To do so, go to the “Class Name” drop-down list and select “mWorker”, then select “RunWorkerCompleted” from the “Method Name” drop-down list, as follows:
Now that we have the mWorker_RunWorkerCompleted method added to our code window, let’s go to it and insert code to retrieve the filled table from the BackgroundWorker thread, and use it to fill a DataGridView.
By the way, now’s a good time to go to your form design view, remove the TextBox, then add a DataGridView to the form, leaving it named “DataGridView1".
Here’s an example:
Now that we have all of the code added, let’s take a look at the full Form1 class to see what it looks like:
Ok, so are you ready to run it? Let’s give it a whirl. Start your application (F5). When the form comes up, press the button to fill the DataGridView.
You’ll notice that the form is completely responsive while the data is being retrieved on a separate thread using the BackGroundWorker. The user is free to move the form around, and interact with any other controls that may be on the form.
Here’s what my form looked like after the data was retrieved:
The BackgroundWorker class in the System.ComponentModel namespace is very easy to use to perform processor intensive operations on a separate dedicated thread.
With very little knowledge of threading, you can begin to implement multi-threading into your applications, greatly enhancing their responsiveness during time-consuming operations.
For more information on the BackgroundWorker, please see the MSDN Documentation at http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx