码迷,mamicode.com
首页 > 其他好文 > 详细

BackgroundWorker Class Sample for Beginners

时间:2019-04-21 12:56:12      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:abs   namespace   org   execution   sig   idt   cep   contains   rsh   

Introduction

This article presents a novice .NET developer to develop a multithreading application without being burdened by the complexity that comes with?threading.

Background

A basic Windows application runs on a single?thread?usually referred to as UI?thread. This UI?thread?is responsible for creating/painting all the controls and upon which the code execution takes place. So when you are running a long-running task (i.e., data intensive database operation or processing some 100s of bitmap images), the UI?thread?locks up and the UI application turns white (remember the UI?thread?was responsible to paint all the controls) rendering your application to Not Responding state.

技术图片

Using the Code

What you need to do is to shift this heavy processing on a different?thread.

技术图片

Leave the UI?thread?free for painting the UI. .NET has made the?BackgroundWorker?object available to us to simplify?threading. This object is designed to simply run a function on a different?thread?and then call an event on your UI?thread?when it‘s complete.

The steps are extremely simple:

  1. Create a?BackgroundWorker?object.
  2. Tell the?BackgroundWorker?object what task to run on the background?thread?(the?DoWork?function).
  3. Tell it what function to run on the UI?thread?when the work is complete (the?RunWorkerCompleted?function).

BackgroundWorker?uses the?thread-pool, which recycles?threads to avoid recreating them for each new task. This means one should never call?Abort?on a?BackgroundWorker?thread.

And a golden rule never to forget:

Never access UI objects on a?thread?that didn‘t create them. It means you cannot use a code such as this...

lblStatus.Text = "Processing file...20%";

...in the?DoWork?function. Had you done this, you would receive a runtime error. The?BackgroundWorker?object resolves this problem by giving us a?ReportProgress?function which can be called from the background?thread‘sDoWork?function. This will cause the?ProgressChanged?event to fire on the UI?thread. Now we can access the UI objects on their?thread?and do what we want (In our case, setting the label text status).

BackgroundWorker?also provides a?RunWorkerCompleted?event which fires after the?DoWork?event handler has done its job. Handling?RunWorkerCompleted?is not mandatory, but one usually does so in order to query any exception that was thrown in?DoWork. Furthermore, code within a?RunWorkerCompleted?event handler is able to update Windows Forms and WPF controls without explicit marshalling; code within the?DoWork?event handler cannot.

To add support for?progress?reporting:

  • Set the?WorkerReportsProgress?property to?true.
  • Periodically call?ReportProgress?from within the?DoWork?event handler with a "percentage complete" value.?
    m_oWorker.ReportProgress(i); //as seen in the code 
  • Handle the?ProgressChanged?event, querying its event argument‘s?ProgressPercentage?property:?
    void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // This function fires on the UI thread so it‘s safe to edit
        // the UI control directly, no funny business with Control.Invoke :)
        // Update the progressBar with the integer supplied to us from the
        // ReportProgress() function.
        progressBar1.Value = e.ProgressPercentage;
        lblStatus.Text = "Processing......" + progressBar1.Value.ToString() + "%";
    } 

Code in the?ProgressChanged?event handler is free to interact with UI controls just as with?RunWorkerCompleted. This is typically where you will update a?progress?bar.

To add support for cancellation:

  • Set the?WorkerSupportsCancellation?property to?true.
  • Periodically check the?CancellationPending?property from within the?DoWork?event handler – if?true, set the event argument‘s?Cancel?property to?true, and return. (The worker can set?Cancel true?and exit without prompting via?CancellationPending?– if it decides the job‘s too difficult and it can‘t go on).
     if (m_oWorker.CancellationPending)
      {
         // Set the e.Cancel flag so that the WorkerCompleted event
         // knows that the process was cancelled.
         e.Cancel = true;
         m_oWorker.ReportProgress(0);
         return;
      } 
  • Call?CancelAsync?to request cancellation. This code is handled on click of the Cancel button.

     m_oWorker.CancelAsync(); 

Properties

This is not an exhaustive list, but I want to emphasize the?Argument,?Result, and the?RunWorkerAsync?methods. These are properties of?BackgroundWorker?that you absolutely need to know to accomplish anything. I show the properties as you would reference them in your code.

  • DoWorkEventArgs e?Usage: Contains?e.Argument?and?e.Result, so it is used to access those properties.
  • e.Argument?Usage: Used to get the parameter reference received by?RunWorkerAsync.
  • e.Result?Usage: Check to see what the?BackgroundWorker?processing did.
  • m_oWorker.RunWorkerAsync();?Usage: Called to start a process on the worker?thread.

Here‘s the entire code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Threading;
using System.Text;
using System.Windows.Forms;

namespace BackgroundWorkerSample
{
    // The BackgroundWorker will be used to perform a long running action
    // on a background thread.  This allows the UI to be free for painting
    // as well as other actions the user may want to perform.  The background
    // thread will use the ReportProgress event to update the ProgressBar
    // on the UI thread.
    public partial class Form1 : Form
    {
        /// <summary>
        /// The backgroundworker object on which the time consuming operation 
        /// shall be executed
        /// </summary>
        BackgroundWorker m_oWorker;

        public Form1()
        {
            InitializeComponent();
            m_oWorker = new BackgroundWorker();
    
            // Create a background worker thread that ReportsProgress &
            // SupportsCancellation
            // Hook up the appropriate events.
            m_oWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork);
            m_oWorker.ProgressChanged += new ProgressChangedEventHandler
					(m_oWorker_ProgressChanged);
            m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler
					(m_oWorker_RunWorkerCompleted);
            m_oWorker.WorkerReportsProgress = true;
            m_oWorker.WorkerSupportsCancellation = true;
        }

        /// <summary>
        /// On completed do the appropriate task
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_oWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // The background process is complete. We need to inspect
            // our response to see if an error occurred, a cancel was
            // requested or if we completed successfully.  
            if (e.Cancelled)
            {
                lblStatus.Text = "Task Cancelled.";
            }

            // Check to see if an error occurred in the background process.

            else if (e.Error != null)
            {
                lblStatus.Text = "Error while performing background operation.";
            }
            else
            {  
                // Everything completed normally.
                lblStatus.Text = "Task Completed...";
            }

            //Change the status of the buttons on the UI accordingly
            btnStartAsyncOperation.Enabled = true;
            btnCancel.Enabled = false;
        }

        /// <summary>
        /// Notification is performed here to the progress bar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_oWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {

            // This function fires on the UI thread so it‘s safe to edit

            // the UI control directly, no funny business with Control.Invoke :)

            // Update the progressBar with the integer supplied to us from the

            // ReportProgress() function.  

            progressBar1.Value = e.ProgressPercentage;
            lblStatus.Text = "Processing......" + progressBar1.Value.ToString() + "%";
        }

        /// <summary>
        /// Time consuming operations go here </br>
        /// i.e. Database operations,Reporting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // The sender is the BackgroundWorker object we need it to
            // report progress and check for cancellation.
            //NOTE : Never play with the UI thread here...
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(100);

                // Periodically report progress to the main thread so that it can
                // update the UI.  In most cases you‘ll just need to send an
                // integer that will update a ProgressBar                    
                m_oWorker.ReportProgress(i);
                // Periodically check if a cancellation request is pending.
                // If the user clicks cancel the line
                // m_AsyncWorker.CancelAsync(); if ran above.  This
                // sets the CancellationPending to true.
                // You must check this flag in here and react to it.
                // We react to it by setting e.Cancel to true and leaving
                if (m_oWorker.CancellationPending)
                {
                    // Set the e.Cancel flag so that the WorkerCompleted event
                    // knows that the process was cancelled.
                    e.Cancel = true;
                    m_oWorker.ReportProgress(0);
                    return;
                }
            }

            //Report 100% completion on operation completed
            m_oWorker.ReportProgress(100);
        }

        private void btnStartAsyncOperation_Click(object sender, EventArgs e)
        {
            //Change the status of the buttons on the UI accordingly
            //The start button is disabled as soon as the background operation is started
            //The Cancel button is enabled so that the user can stop the operation 
            //at any point of time during the execution
            btnStartAsyncOperation.Enabled = false;
            btnCancel.Enabled = true;

            // Kickoff the worker thread to begin it‘s DoWork function.
            m_oWorker.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (m_oWorker.IsBusy)
            {

                // Notify the worker thread that a cancel has been requested.

                // The cancel will not actually happen until the thread in the

                // DoWork checks the m_oWorker.CancellationPending flag. 

                m_oWorker.CancelAsync();
            }
        }
    }
}

1. Start

Once the application is started, click on the button that reads Start Asynchronous Operation. The UI now shows aprogressbar with UI continuously being updated.

技术图片

2. Cancel

To cancel the parallel operation midway, press the cancel button. Note that the UI?thread?is now free to perform any additional task during this time and it is not locked by the data intensive operation that is happening in the background.

技术图片

3. On Successful Completion

技术图片

The statusbar shall read Task Completed upon the successful completion of the parallel task.

Points of Interest



转自:http://www.codeproject.com/Articles/99143/BackgroundWorker-Class-Sample-for-Beginners

BackgroundWorker Class Sample for Beginners

标签:abs   namespace   org   execution   sig   idt   cep   contains   rsh   

原文地址:https://www.cnblogs.com/xfgnongmin/p/10744624.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
分享档案
周排行
mamicode.com排行更多图片
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!