The importance of the Background Worker component


Serious Problem

I recently reviewed a very large enterprise application for one of the biggest retail companies in the country, and was aghast at the lack of multi-threading in the application. I resolved to ensure I post about it, though this subject has been covered numerous times over the years, and there are a few examples interspersed over the internet. New developers or maybe intermediate developers not working in the data access layer of a project may gloss over why this component is so important. You also don’t want to revisit an application in a few years time and have to make multi-threading modifications.

I came to realise the true importance of this component, because as I spoke with staff, and they talked me through the program working, at key stages during the program I would be told that after clicking a button that makes a database call  “the computer is thinking now, so wait for a while” or “when you click this, you cannot do anything”, or “don’t click that option because it will freeze the screen, and you won’t be able to use the computer for half an hour”.

The list of these warnings goes on and on, about using their system. The administration staff/information workers have tasks they have to run several times every day, and typically, they cannot use their system for half and hour or even more at times, because the queries they are running are executing on the UI thread. This quite frankly is an atrocious situation. Pretty much every part of their application that makes database calls, runs on the UI thread.

What ‘beggars belief’ is that this system is used by one of the largest retail companies in the UK, and that there are doubtlessly more retailers that are in the same boat. These retail organisations realised the power of computing many years ago, and their systems were developed at a time when patterns and practices had not been invented, and some very bad coding practices were cultivated. I estimate that this specific company that has triggered this post, is losing at least £100 000 annually in wages and lost productivity, because all their staff spend a third of their day twiddling their thumbs, while their computer screens are frozen because the UI thread is locked up (and this is a conservative estimate).

Demonstration

Seriously, if you develop any application that makes calls to a database you must learn to love and use this component on every form that makes a database call!

Typically you will create an interface that has a loadDataBackgroundWorker (when the form loads) and a saveDataBackgroundWorker (when the form is closed). There really is no excuse not to use this component, which incidentally, is framework agnostic, and is used in the new Silverlight 2 runtime. I know of some people that have run into issues with the dispatcher component in WPF, and they are using this in their WPF applications. In general, writing multithreaded applications is very difficult, the background worker component makes writing multithreaded applications so much easier.

To demonstrate how this works, create a new windows forms application in C# or Visual Basic (you can use the express editions) and drag a progress bar and two buttons onto the form. Name the appropriate button “startButton” and “MessageButton”.

TheForm

In the code behind, handle the events thus (Visual Basic example is beneath)

C#

using System;

using System.ComponentModel;

using System.Windows.Forms;

 

namespace BackgroundWorkerDemo

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

 

            this.progressBar1.Minimum = 0;

            this.progressBar1.Maximum = 9;

            this.progressBar1.Value = 0;

        }

 

        private void messageButton_Click(object sender, EventArgs e)

        {

            MessageBox.Show("Hello World!");

        }

 

        private void startButton_Click(object sender, EventArgs e)

        {

            for (int i = 1; i < 10; i++)

            {

                this.progressBar1.Value = i;

                System.Threading.Thread.Sleep(1000);

            }

        }

    }

}

Visual Basic

Imports System.ComponentModel

 

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Me.progressBar1.Minimum = 0

        Me.progressBar1.Maximum = 9

        Me.progressBar1.Value = 0

    End Sub

 

    Private Sub messageButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles messageButton.Click

        MessageBox.Show("Hello World!")

    End Sub

 

    Private Sub startButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles startButton.Click

        For i As Integer = 1 To 9

            Me.progressBar1.Value = i

            System.Threading.Thread.Sleep(1000)

        Next

    End Sub

End Class

If you run the program, and click on the “show message” button, this shows the message “Hello World!”. If you then click on the start button and try to click the “show message” button you will find that you cannot trigger the message, until the progress bar has completed. Incidentally, System.Threading.Thread.Sleep(1000) just causes the thread the progress bar is running on to pause for a second (1000 milliseconds). If this were not included, the for loop would execute too fast, and you would not see the screen locking up.

This scenario is no different to the problem I have outlined by this major enterprise application. When users of the application click “load orders” or whatever option fetched data from the database, their application is locking the UI thread, and this leads to a very poor user experience, and statements like “the computer is thinking about things”.

Solution

Drag a background worker component onto your form from the toolbox, and set the reports progress property to true

ReportsProgress

Click the events button and handle all 3 events

BackGroundEvents

In the event handlers move the previous code so it looks like this

C#

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

 

namespace BackgroundWorkerDemo

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

 

            this.progressBar1.Minimum = 0;

            this.progressBar1.Maximum = 9;

            this.progressBar1.Value = 0;

        }

 

        private void messageButton_Click(object sender, EventArgs e)

        {

            MessageBox.Show("Hello World!");

        }

 

        private void startButton_Click(object sender, EventArgs e)

        {

            this.backgroundWorker1.RunWorkerAsync();

        }

 

 

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

        {

            for (int i = 1; i < 10; i++)

            {

                this.backgroundWorker1.ReportProgress(i);

                System.Threading.Thread.Sleep(1000);

            }

        }

 

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

        {

            this.progressBar1.Value = (int)e.ProgressPercentage;

        }

 

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

        {

            this.progressBar1.Value = 0;

        }

    }

}

Visual Basic

Imports System.ComponentModel

 

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Me.progressBar1.Minimum = 0

        Me.progressBar1.Maximum = 9

        Me.progressBar1.Value = 0

    End Sub

 

    Private Sub messageButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

    Handles messageButton.Click

        MessageBox.Show("Hello World!")

    End Sub

 

    Private Sub startButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

    Handles startButton.Click

        Me.BackgroundWorker1.RunWorkerAsync()

    End Sub

 

    Private Sub backgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) _

    Handles BackgroundWorker1.DoWork

        For i As Integer = 1 To 9

            Me.BackgroundWorker1.ReportProgress(i)

            System.Threading.Thread.Sleep(1000)

        Next

    End Sub

 

    Private Sub backgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As ProgressChangedEventArgs) _

    Handles BackgroundWorker1.ProgressChanged

        Me.progressBar1.Value = CInt(e.ProgressPercentage)

    End Sub

 

    Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As RunWorkerCompletedEventArgs) _

    Handles BackgroundWorker1.RunWorkerCompleted

        progressBar1.Value = 0

    End Sub

End Class

When you run the application and click the message button after you click start, the long task (i.e. the work done connecting to your database) is executed on another thread asynchronously. this results in a responsive application that does not lock. The users at the aforementioned enterprise can then click into other options in their application and do other work, while the program processes.

Quick Tip : Ensure that you do not add any code that accesses the UI thread in the DoWork event. You may have a label that says connecting…. in your ToolStripLabel for instance. Make sure this code is where the backgroundWorker.RunWorker.Async() is called, and nowhere else. You can add a message so say “completed” or something similar in the RunWorkerCompleted event as that is on the UI thread.

I really cannot reiterate sufficiently clearly just how important it is to wrap every method in your application that makes a database call in a background worker. Not only  does this improve the efficiency and productivity of employees (users), but they come away with a very positive view of the application, which is not what the users at this company have. When training new staff they end up being on tenterhooks just in case they click on the wrong button, and cause the program to freeze. At busy times, or near the end of the day where work needs to be completed, the last thing you need, is an application that hangs for half an hour or even longer before you can use it again.

Please, please, please, use this component as much as you can in all your data access applications.

3 thoughts on “The importance of the Background Worker component

  1. Ira, I just spent an entire Saturday scouring the internet looking for a good example of a single thread program versus a multithreaded equivalent to show the differences at the most minimal levels.

    You did a fantastic job prepping these scripts to demonstrate this very concept.

    I plugged in both VB.Net scripts into two different projects in VB.Net Express 2010 and was able to get them working within 5 minutes.

    Great Article! -Jeff

  2. Pingback: Writing multithreaded programs using Async & Await in C# « Ira Lukhezo's blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s