Section 3.15. Handle Asynchronous Tasks Safely


3.15. Handle Asynchronous Tasks Safely

One of .NET's most impressive features is its extensive support for multithreaded programming. However, as most programmers discover at some point in their lives, multithreaded programming isn't necessarily easy.


Note: Need to conduct a time-consuming task in the background without dealing with threading issues? The new BackgroundWorker class makes it easy.

One of the main challenges with Windows applications is that it's not safe to modify a form or control from a background thread, which means that after your background task is finished, there's no straightforward way to update your application's interface. You can use the Control.Invoke( ) method to marshal a method to the correct thread, but other problems then appear, such as transferring the information you need to make the update. Fortunately, all of these headaches can be avoided thanks to the new BackgroundWorker component.

3.15.1. How do I do that?

The BackgroundWorker component gives you a foolproof way to run a time-consuming task on a separate, dedicated thread. This ensures that your application interface remains responsive, and it allows your code to carry out other tasks in the foreground. Best of all, the underlying complexities of multithreaded programming are hidden. Once the background process is complete, you simply handle an event, which fires on the main thread. In addition, the BackgroundWorker supports progress reporting and canceling.

You can either create a BackgroundWorker object programmatically, or you can drag it onto a form from the Components tab of the toolbox. To start your background operation, you call the RunWorkerAsync( ) method. If you need to pass an input value to this process, you can supply it as an argument to this method (any type of object is allowed):

Worker.RunWorkerAsync(inputValue)

Next, you need to handle the DoWork event to perform the background task. The DoWork event fires on the background thread, which means at this point you can't interact with any other part of your application (unless you're willing to use locks or other techniques to safeguard access). Typically, the DoWork event handler retrieves the input value from the DoWorkEventArgs.Argument property and then carries out the time-consuming operation. Once the operation is complete, you simply set the DoWorkEventArgs.Result property with the result. You can use any data type or even a custom object. Here's the basic pattern you'll use:

Private Sub backgroundWorker1_DoWork(ByVal sender As Object, _   ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork          ' Get the information that was supplied.     Dim Input As Integer = CType(e.Argument, Integer)          ' (Perform some time consuming task.)          ' Return the result.     e.Result = Answer      End Sub

Finally, the BackgroundWorker fires a RunWorkerCompleted event to notify your application that the process is complete. At this point, you can retrieve the result from RunWorkerCompletedEventArgs and update the form accordingly:

Private Sub backgroundWorker1_RunWorkerCompleted( _   ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _   Handles backgroundWorker1.RunWorkerCompleted          result.Text = "Result is: " & e.Result.ToString( )      End Sub

Example 3-6 shows a form that puts all of these parts together. It performs a time-limited loop for a number of seconds that you specify. This example also demonstrates two more advanced techniques: cancellation and progress. To cancel the operation, you simply need to call the BackgroundWorker.CancelAsync( ) method. Your DoWork event-handling code can then check to see if the main form is attempting to cancel the operation and exit gracefully. To maintain progress information, your DoWork event-handling code needs to call the BackgroundWorker.ReportProgress( ) method and provide an estimated percent complete (where 0% means "just started" and 100% means "completely finished"). The form code can respond to the ProgressChanged event to read the new progress percentage and update another control, such as a ProgressBar. Figure 3-12 shows this application in action.

Example 3-6. An asynchronous form with the BackgroundWorker
Public Class AsyncForm          Private Sub startAsyncButton_Click(ByVal sender As System.Object, _       ByVal e As System.EventArgs) Handles startAsyncButton.Click              ' Disable the Start button until          ' the asynchronous operation is done.         startAsyncButton.Enabled = False              ' Enable the Cancel button while          ' the asynchronous operation runs.         cancelAsyncButton.Enabled = True              ' Start the asynchronous operation.         backgroundWorker1.RunWorkerAsync(Int32.Parse(txtWaitTime.Text))     End Sub          ' This event handler is where the actual work is done.     Private Sub backgroundWorker1_DoWork(ByVal sender As Object, _       ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork              ' Get the information that was supplied.         Dim Worker As BackgroundWorker = CType(sender, BackgroundWorker)              Dim StartTime As DateTime = DateTime.Now         Dim SecondsToWait As Integer = CType(e.Argument, Integer)         Dim Answer As Single = 100         Do             ' Check for any cancellation requests.             If Worker.CancellationPending Then                 e.Cancel = True                 Return             End If                  ' Continue calculating the answer.             Answer *= 1.01                  ' Report the current progress (percentage complete).             Worker.ReportProgress(( _               DateTime.Now.Subtract(StartTime).TotalSeconds / SecondsToWait) * 100)                  Thread.Sleep(50)         Loop Until DateTime.Now > (StartTime.AddSeconds(SecondsToWait))              e.Result = Answer     End Sub          ' This event handler fires when the background work     ' is complete.     Private Sub backgroundWorker1_RunWorkerCompleted( _       ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _       Handles backgroundWorker1.RunWorkerCompleted              ' Check what the result was, and update the form.         If Not (e.Error Is Nothing) Then             ' An exception was thrown.             MessageBox.Show(e.Error.Message)         ElseIf e.Cancelled Then             ' Check if the user cancelled the operation.             result.Text = "Cancelled"         Else             ' The operation succeeded.             result.Text = "Result is: " & e.Result.ToString( )         End If              startAsyncButton.Enabled = True         cancelAsyncButton.Enabled = False     End Sub          ' This event handler updates the progress bar.     Private Sub backgroundWorker1_ProgressChanged( _       ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _       Handles backgroundWorker1.ProgressChanged              Me.progressBar1.Value = e.ProgressPercentage     End Sub          Private Sub cancelAsyncButton_Click( _       ByVal sender As System.Object, ByVal e As System.EventArgs) _       Handles cancelAsyncButton.Click              ' Cancel the asynchronous operation.         Me.backgroundWorker1.CancelAsync( )              cancelAsyncButton.Enabled = False     End Sub       End Class

Figure 3-12. Monitoring a background task


3.15.2. What about...

...other scenarios where you can use the BackgroundWorker? This example used the BackgroundWorker with a long-running background calculation. Other situations in which the BackgroundWorker proves to be just as indispensable include:

  • Contacting a web service

  • Downloading a file over the Internet

  • Retrieving data from a database

  • Reading or writing large amounts of data

3.15.3. Where can I learn more?

The MSDN reference includes a detailed walkthrough for using the BackgroundWorker, and other topics that tackle multithreaded programming in detail. Look up the "background operations" index entry to see a slightly different approach that uses the BackgroundWorker to calculate Fibonacci numbers.



Visual Basic 2005(c) A Developer's Notebook
Visual Basic 2005: A Developers Notebook
ISBN: 0596007264
EAN: 2147483647
Year: 2006
Pages: 123

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net