C# Tutorial - Using the BackgroundWorker Class

It's a rule that should never be forgotten - don't ever perform work that takes a non-trivial amount of time on the UI thread. Of course you're now wondering, "Where do I perform tasks that take a non-trivial amount of time?". The answer is simple - on a different thread. There are lots of ways to get your work onto another thread, which can include directly creating a Thread or using the ThreadPool. This tutorial, however, will introduce you to .NET's BackgroundWorker class and how it can be used to get the job done.

The other common techniques, such as using Thread or Threadpool, have the downside that you're explicitly required to invoke the Dispatcher whenever you need to update the user interface. The BackgroundWorker class does the invocation for you, which can make things a little nicer.

Below is a Silverlight application that demonstrates the usefulness of performing work on background threads. Remember though, that the code I used here is not specific to Silverlight - it will work on any .NET v2.0 or higher application.



The first thing we're going to take a look at is the function that gets called when you the click the UI thread button.

private void UIThreadButtonClick ( object sender, RoutedEventArgs e )
{
_uiButton. IsEnabled = false ;
DoUIThreadWork ( ) ;
}

/// <summary>
/// Performs work directly on the UI thread.
/// </summary>
private void DoUIThreadWork ( )
{
int totalSteps = 5 ;

for ( int i = 1 ; i <= totalSteps ; i ++ )
{
// Sleep the thread, which simulates doing something
// expensive - like a network request.
Thread. Sleep ( 1000 ) ;

_uiProgressBar. Value = ( ( double )i / ( double )totalSteps ) * 100 ;
}

_uiButton. IsEnabled = true ;
}

As you can see, I simply loop through 5 iterations of "work". Each piece takes 1 second to complete. I simulate actual work by using a sleep. If you ran the example, you'll notice that you don't actually get any progress updates whatsoever. This is because the UI thread is completely tied up doing your work and doesn't have any time for itself to redraw the screen. If this were an actual desktop application, the user would experience a frozen program that no longer responds.

Now let's check out the other example:

private void BackgroundWorkerButtonClick ( object sender, RoutedEventArgs e )
{
_backgroundButton. IsEnabled = false ;
DoBackgroundWork ( ) ;
}

/// <summary>
/// Creates a BackgroundWorker class to do work
/// on a background thread.
/// </summary>
private void DoBackgroundWork ( )
{
BackgroundWorker worker = new BackgroundWorker ( ) ;

// Tell the worker to report progress.
worker. WorkerReportsProgress = true ;

worker. ProgressChanged += ProgressChanged ;
worker. DoWork += DoWork ;
worker. RunWorkerCompleted += WorkerCompleted ;
worker. RunWorkerAsync ( ) ;
}

/// <summary>
/// The work for the BackgroundWorker to perform.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DoWork ( object sender, DoWorkEventArgs e )
{
BackgroundWorker worker = sender as BackgroundWorker ;

int totalSteps = 5 ;

for ( int i = 1 ; i <= totalSteps ; i ++ )
{
// Sleep the thread, which simulates doing something
// expensive - like a network request.
Thread. Sleep ( 1000 ) ;

// Tell the BackgroundWorker to report a new progress.
worker. ReportProgress ( ( int ) ( ( ( double )i / ( double )totalSteps ) * 100 ) ) ;
}
}

/// <summary>
/// Occurs when the BackgroundWorker reports a progress.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ProgressChanged ( object sender, ProgressChangedEventArgs e )
{
_backgroundProgressBar. Value = e. ProgressPercentage ;
}

/// <summary>
/// Occurs when the BackgroundWorker has completed its work.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void WorkerCompleted ( object sender, RunWorkerCompletedEventArgs e )
{
_backgroundButton. IsEnabled = true ;
}

There's quite a bit more code here, but unlike the previous example, this one does update the user interface as work is being completed. The setup for the BackgroundWorker object should be pretty straight forward. If you want the object to be able to report progress (which we do) you'll need to make sure to set the property, WorkerReportsProgress, to true. If you don't, you'll get an exception when you attempt to report progress.

Most of the logic for a BackgroundWorker class is split among three events: DoWork, ProgressChanged, and RunWorkerCompleted. DoWork is called when the BackgroundWorker is ready to do some work. It's also where you should put all of the code you'd like executed on the background thread. In this example, it does the exact same work as the previous one, except now it invokes ReportProgress when it wants to notify the UI.

The ProgressChanged event gets fired when when ReportProgress is called. This event is automatically dispatched to the UI thread, so there's no need to use invoke to update user interface elements. In my example, I simply update the progress bar's value to the reported progress.

The last event, RunWorkerCompleted, is pretty self-explanatory. When the background thread exits, the event will be fired.

And that's it. Using the BackgroundWorker class is very simple, but has the potential to make your application much more responsive. By not requiring the use of invokes, it is sometimes a better choice that more mainstream background processing techniques. You can download a Visual Studio 2008 solution with all of the source code for my example below.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值