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...

 Collapse | Copy
Code
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. 
     Collapse | Copy
    Code
    m_oWorker.ReportProgress(i); //as seen in the code 
  • Handle the ProgressChanged event,
    querying its event argument's ProgressPercentage property: 
     Collapse | Copy
    Code
    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).

     Collapse | Copy
    Code
     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.

     Collapse | Copy
    Code
     m_oWorker.CancelAsync(); 

Properties

This is not an exhaustive list, but I want to emphasize the ArgumentResult,
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:

 Collapse | Copy
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的更多相关文章

  1. Multithreading annd Grand Central Dispatch on ios for Beginners Tutorial-多线程和GCD的入门教程

    原文链接:Multithreading and Grand Central Dispatch on iOS for Beginners Tutorial Have you ever written a ...

  2. TSQL Beginners Challenge 3 - Find the Factorial

    这是一个关于CTE的应用,这里我们用CTE实现阶乘 Factorial,首先来看一个简单的小实验,然后再来看题目.有的童鞋会问怎么没有2就来3了呢,惭愧,TSQL Beginners Challeng ...

  3. GDI+ Tutorial for Beginners

    原文 GDI+ Tutorial for Beginners GDI+ is next evolution of GDI. Using GDI objects in earlier versions ...

  4. ObjectARX® for Beginners: An Introduction

    转:ObjectARX® for Beginners: An Introduction Lee Ambrosius – Autodesk, Inc.         CP4164-L    Objec ...

  5. BackgroundWorker study

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  6. C# BackgroundWorker 详解

    在C#程序中,经常会有一些耗时较长的CPU密集型运算,如果直接在 UI 线程执行这样的运算就会出现UI不响应的问题.解决这类问题的主要途径是使用多线程,启动一个后台线程,把运算操作放在这个后台线程中完 ...

  7. Linux下UPnP sample分析

        一.UPnP简介   UPnP(Universal Plug and Play)技术是一种屏蔽各种数字设备的硬件和操作系统的通信协议.它是一种数字网络中间件技术,建立在TCP/IP.HTTP协 ...

  8. 【Winform】使用BackgroundWorker控制进度条显示进度

    许多开发者看见一些软件有进度条显示进度,自己想弄,项目建好后发现并没有自己想象中的那么简单...看了网上很多教程后,写了一个小Demo供网友们参考~~,Demo的网址:http://pan.baidu ...

  9. cocos2d-x for android配置 & 运行 Sample on Linux OS

    1.从http://www.cocos2d-x.org/download下载稳定版 比如cocos2d-x-2.2 2.解压cocos2d-x-2.2.zip,比如本文将其解压到 /opt 目录下 3 ...

随机推荐

  1. MeshLab 编译

    1.需要以下:  MeshLab 1.3.3  下载地址 http://sourceforge.net/projects/meshlab/files/meshlab Win7 X64  Visual ...

  2. Jmeter响应中中文乱码怎么解决

    在jmeter的bin目录下有一个jmeter.properties的文件,打开它,搜索sampleresult.default.encoding,把它的注释打开,也就是把最前面的#去掉,改成samp ...

  3. Java基础-常用工具类(一)

    object类 Object 类是所有类的的父类,如果一个类没有明确使用EXPENTS关键字明确标识继承另外一个类,那么这个类默认继承object类,oject类中的方法适合所有子类 1)toStri ...

  4. jvm的基本结构以及各部分详解(转)

    原文链接:https://www.cnblogs.com/zwbg/p/6194470.html 1.java虚拟机的基本结构 图: 1.类加载器子系统从文件系统或者网络中加载Class信息,类信息( ...

  5. Linux 虚拟内存机制

    每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构. Linux内存管理采用的是页式管理,使用的是多级页表,动态地址转换机构与主存.辅存共同实现虚拟内存 一个新进程建立的时候,将会建 ...

  6. NioEventLoop中的thread什么时候启动

    在构造函数中被赋值,并传入传入runnable接口,方法里面循环select,然后处理找到的key 但是这个thread是什么时候被start的呢? 在bootstrap bind的逻辑里,后半部分是 ...

  7. FZU 1759-Super A^B mod C

    传送门:http://acm.fzu.edu.cn/problem.php?pid=1759 Accept: 1161    Submit: 3892Time Limit: 1000 mSec     ...

  8. 2.28 查看webdriver API

    2.28 查看webdriver API(带翻译) 前言    前面都是点点滴滴的介绍selenium的一些api使用方法,那么selenium的api到底有多少呢?本篇就教大家如何去查看seleni ...

  9. 普通new和placement new的重载

    对于自定义对象,我们可以重载普通new操作符,这时候使用new Test()时就会调用到我们重载的普通new操作符. 示例程序: #include <iostream> #include ...

  10. 阿里云CentOS中vsftp安装、配置、卸载

    1--卸载 查看当前服务器中的vsftpdrpm -qa|grep vsftpd 例如结果为:vsftpd-2.2.2-13.el6_6.1.x86_64执行卸载rpm -e vsftpd-2.2.2 ...