March 20, 2010 by Christoff Truter 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.
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;
}
}
}
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();
}
void addItem(Int32 i)
{
if (lbxItems.InvokeRequired)
{
Action<Int32> a = new Action<int>(addItem);
this.BeginInvoke(a, i);
}
else
{
lbxItems.Items.Insert(0, i);
}
}