C# Threading: BackgroundWorker

March 20, 2010 by C#   Threading  

Running intensive tasks like database transactions, uploads/downloads etc, on the same thread as your UI, can cause your UI to become unresponsive.

To solve this issue, we can move these intensive tasks to separate/background threads.

For this purpose we can use the BackgroundWorker component/class, which you can drop to your form from the toolbox within Visual Studio - or simply create an instance of the BackgroundWorker class.

Lets have a quick look, at the demo code that you can download at the top of the post. We're going to create a small little application like this:


BackgroundWorker bw = new BackgroundWorker();

public mainForm()
{
	InitializeComponent();
	bw.DoWork += new DoWorkEventHandler(bw_DoWork);
	bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
	bw.WorkerReportsProgress = true;
	bw.WorkerSupportsCancellation = true;
}

private void btnGo_Click(object sender, EventArgs e)
{
	if (cbxWorker.Checked)
	{
		if (!bw.IsBusy) // If the thread isn't started, start it
		{
			bw.RunWorkerAsync();
		}
	}
	else
	{
		// What would happen if we didn't use a background thread?
		for (int i = 0; i < 50; i++)
		{
			Thread.Sleep(100);
			lbxItems.Items.Insert(0, i);
		}
	}
}
Notice the attached events, DoWork & ProgressChanged.

The method handling the DoWork event runs within our background thread, making it the ideal spot to run intensive tasks, it is where we do the work (as the name suggests).

void bw_DoWork(object sender, DoWorkEventArgs e)
{
	for (int i = 0; i < 50; i++)
	{
		Thread.Sleep(100); // Some intensive task

		if (bw.CancellationPending)
		{
			e.Cancel = true;
		}

		if (!e.Cancel)
		{
			bw.ReportProgress(i); // Inform the main thread of our progress
		}
		else
		{
			break;
		}
	}
}

The method handling the ProgressChanged event runs within the thread where our UI resides, which makes it the ideal place where we can report whats currently happening in our background thread - e.g. access form controls etc.

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
	lbxItems.Items.Insert(0, e.ProgressPercentage);
}
You might have noticed the CancellationPending property in the bw_DoWork method, this property informs us if the background process must still continue or if its been cancelled - which allows us to gracefully handle the cancellation of the background thread.

private void btnStop_Click(object sender, EventArgs e)
{
	bw.CancelAsync();
}

If you really really need to access controls that resides in the thread handling the UI from your background thread (and not the ProgressChanged event), you can do it like this.

void addItem(Int32 i)
{
	if (lbxItems.InvokeRequired)
	{
		Action<Int32> a = new Action<int>(addItem);
		this.BeginInvoke(a, i);
	}
	else
	{
		lbxItems.Items.Insert(0, i);
	}
}

Notice the InvokeRequired property, this tells us if the control we're trying to access is available within our current context, if its not we resend our caller via delegate to the BeginInvoke method, until we've contacted the thread where our control resides in.

There is also an event called RunWorkerCompleted, that gets triggered as soon as our backgroundworker completes.

There is a lot of things to consider when designing applications that use threads like race conditions, deadlocks, livelocks, starvation etc - perhaps something I can write about in a future post - but this post should give you a basic idea of how to get started at least.


Leave a Comment




Related Downloads

C# .NET BackgroundWorker Demo