Export (0) Print
Expand All
4 out of 23 rated this helpful - Rate this topic

Walkthrough: Implementing a Form That Uses a Background Operation 

If you have an operation that will take a long time to complete, and you do not want your user interface (UI) to stop responding or "hang," you can use the BackgroundWorker class to execute the operation on another thread.

This walkthrough illustrates how to use the BackgroundWorker class to perform time-consuming computations "in the background," while the user interface remains responsive. When you are through, you will have an application that computes Fibonacci numbers asynchronously. Even though computing a large Fibonacci number can take a noticeable amount of time, the main UI thread will not be interrupted by this delay, and the form will be responsive during the calculation.

Tasks illustrated in this walkthrough include:

  • Creating a Windows-based Application

  • Creating a BackgroundWorker in Your Form

  • Adding Asynchronous Event Handlers

  • Adding Progress Reporting and Support for Cancellation

For a complete listing of the code used in this example, see How to: Implement a Form That Uses a Background Operation.

NoteNote

The dialog boxes and menu commands you see might differ from those described in Help depending on your active settings or edition. To change your settings, choose Import and Export Settings on the Tools menu. For more information, see Visual Studio Settings.

The first step is to create the project and to set up the form.

To create a form that uses a background operation

  1. Create a Windows-based application project called BackgroundWorkerExample. For details, see How to: Create a Windows Application Project.

  2. In Solution Explorer, right-click Form1 and select Rename from the shortcut menu. Change the file name to FibonacciCalculator. Click the Yes button when you are asked if you want to rename all references to the code element 'Form1'.

  3. Drag a NumericUpDown control from the Toolbox onto the form. Set the Minimum property to 1 and the Maximum property to 91.

  4. Add two Button controls to the form.

  5. Rename the first Button control startAsyncButton and set the Text property to Start Async. Rename the second Button control cancelAsyncButton, and set the Text property to Cancel Async. Set its Enabled property to false.

  6. Create an event handler for both of the Button controls' Click events. For details, see How to: Create Event Handlers Using the Designer.

  7. Drag a Label control from the Toolbox onto the form and rename it resultLabel.

  8. Drag a ProgressBar control from the Toolbox onto the form.

You can create the BackgroundWorker for your asynchronous operation using the Windows Forms Designer.

To create a BackgroundWorker with the Designer

  • From the Components tab of the Toolbox, drag a BackgroundWorker onto the form.

You are now ready to add event handlers for the BackgroundWorker component's asynchronous events. The time-consuming operation that will run in the background, which computes Fibonacci numbers, is called by one of these event handlers.

To implement asynchronous event handlers

  1. In the Properties window, click on the Events button. Double-click the DoWork and RunWorkerCompleted events to create event handlers. For more formation about using event handlers, see How to: Create Event Handlers Using the Designer.

  2. Create a new method, called ComputeFibonacci, in your form. This method does the actual work, and it will run in the background. This code demonstrates the recursive implementation of the Fibonacci algorithm, which is notably inefficient, taking exponentially longer time to complete for larger numbers. It is used here for illustrative purposes, to show an operation that can introduce long delays in your application.

    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91.
        // Fib(n), with n > 91, overflows a long.
        if ((n < 0) || (n > 91))
        {
            throw new ArgumentException(
                "value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has canceled.
        // Note that a call to CancelAsync may have set 
        // CancellationPending to true just after the
        // last invocation of this method exits, so this 
        // code will not have the opportunity to set the 
        // DoWorkEventArgs.Cancel flag to true. This means
        // that RunWorkerCompletedEventArgs.Cancelled will
        // not be set to true in your RunWorkerCompleted
        // event handler. This is a race condition.
    
        if (worker.CancellationPending)
        {   
            e.Cancel = true;
        }
        else
        {   
            if (n < 2)
            {   
                result = 1;
            }
            else
            {   
                result = ComputeFibonacci(n - 1, worker, e) + 
                         ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task.
            int percentComplete = 
                (int)((float)n / (float)numberToCompute * 100);
            if (percentComplete > highestPercentageReached)
            {
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    }
    
    
    // This is the method that does the actual work. For this
    // example, it computes a Fibonacci number and
    // reports progress as it does its work.
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91.
        // Fib(n), with n > 91, overflows a long.
        if (n < 0 || n > 91) {
            throw new ArgumentException("value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has cancelled.
        // Note that a call to CancelAsync may have set 
        // CancellationPending to true just after the
        // last invocation of this method exits, so this 
        // code will not have the opportunity to set the 
        // DoWorkEventArgs.Cancel flag to true. This means
        // that RunWorkerCompletedEventArgs.Cancelled will
        // not be set to true in your RunWorkerCompleted
        // event handler. This is a race condition.
        if (worker.get_CancellationPending()) {
        
            e.set_Cancel(true);
        }
        else {
        
            if (n < 2) {
            
                result = 1;
            }
            else {
            
                result = ComputeFibonacci(n - 1, worker, e) 
                    + ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task.
            int percentComplete=(int)((float)(n)/(float)(numberToCompute)* 100);
    
            if (percentComplete > highestPercentageReached) {            
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    } 
    
    
  3. In the DoWork event handler, add a call to the ComputeFibonacci method. Take the first parameter for ComputeFibonacci from the Argument property of the DoWorkEventArgs. The BackgroundWorker and DoWorkEventArgs parameters will be used later for progress reporting and cancellation support.

NoteNote

It is important that the DoWork event handler does not reference the backgroundWorker1 instance variable directly, as this would couple this event handler to a specific instance of BackgroundWorker. Instead, a reference to the BackgroundWorker that raised this event is recovered from the sender parameter. This is important when the form hosts more than one BackgroundWorker.

Assign the return value from ComputeFibonacci to the Result property of the DoWorkEventArgs. This result will be available to the RunWorkerCompleted event handler.

For asynchronous operations that will take a long time, it is often desirable to report progress to the user and to allow the user to cancel the operation. The BackgroundWorker class provides an event that allows you to post progress as your background operation proceeds. It also provides a flag that allows your worker code to detect a call to CancelAsync and interrupt itself.

To implement progress reporting

  1. In the Properties, window, select backgroundWorker1. Set the WorkerReportsProgress and WorkerSupportsCancellation properties to true.

  2. Declare two variables in the FibonacciCalculator form. These will be used to track progress.

    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    
    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
    
  3. Add an event handler for the ProgressChanged event. In the ProgressChanged event handler, update the ProgressBar with the ProgressPercentage property of the ProgressChangedEventArgs parameter.

    // This event handler updates the progress bar.
    private void backgroundWorker1_ProgressChanged(object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }
    
    
    // This event handler updates the progress bar.
    private void backgroundWorker1_ProgressChanged(Object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.set_Value(e.get_ProgressPercentage());
    } //backgroundWorker1_ProgressChanged
    
    

To implement support for cancellation

  1. In the cancelAsyncButton control's Click event handler, add the code that cancels the asynchronous operation.

    private void cancelAsyncButton_Click(System.Object sender, 
        System.EventArgs e)
    {   
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
    
    private void cancelAsyncButton_Click(Object sender, System.EventArgs e)
    {   
        // Cancel the asynchronous operation.
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.set_Enabled(false);
    }
    
    
  2. The following code fragments in the ComputeFibonacci method report progress and support cancellation.

                if (worker.CancellationPending)
                {   
                    e.Cancel = true;
                }
    ...                // Report progress as a percentage of the total task.
                    int percentComplete = 
                        (int)((float)n / (float)numberToCompute * 100);
                    if (percentComplete > highestPercentageReached)
                    {
                        highestPercentageReached = percentComplete;
                        worker.ReportProgress(percentComplete);
                    }
    
    
            if (worker.get_CancellationPending()) {
            
                e.set_Cancel(true);
            }
    ...            // Report progress as a percentage of the total task.
                int percentComplete=(int)((float)(n)/(float)(numberToCompute)* 100);
    
                if (percentComplete > highestPercentageReached) {            
                    highestPercentageReached = percentComplete;
                    worker.ReportProgress(percentComplete);
                }
    
    

At this point, you can compile and run the Fibonacci Calculator application.

To test your project

  • Press F5 to compile and run the application.

    While the calculation is running in the background, you will see the ProgressBar displaying the progress of the calculation toward completion. You can also cancel the pending operation.

    For small numbers, the calculation should be very fast, but for larger numbers, you should see a noticeable delay. If you enter a value of 30 or greater, you should see a delay of several seconds, depending on the speed of your computer. For values greater than 40, it may take minutes or hours to finish the calculation. While the calculator is busy computing a large Fibonacci number, notice that you can freely move the form around, minimize, maximize, and even dismiss it. This is because the main UI thread is not waiting for the calculation to finish.

Now that you have implemented a form that uses a BackgroundWorker component to execute a computation in the background, you can explore other possibilities for asynchronous operations:

  • Use multiple BackgroundWorker objects for several simultaneous operations.

  • To debug your multithreaded application, see How to: Use the Threads Window.

  • Implement your own component that supports the asynchronous programming model. For more information, see Event-based Asynchronous Pattern Overview.

    Caution noteCaution

    When using multithreading of any sort, you potentially expose yourself to very serious and complex bugs. Consult the Managed Threading Best Practices before implementing any solution that uses multithreading.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2014 Microsoft. All rights reserved.