一个通过BackgroundWorker实现WinForm异步操作的例子

在最近的一个Smart
Client项目中,为了演示异步操作的实现,写了一个基于BackgorundWorker的例子。由于这个理基本上实现了BackgorundWorker的大部分功能:异步操作的启动、操作结束后的回调、异步操作的撤销和进度报告等等。尽管没有太多的技术含量,姑且放上来与大家分享。

一、场景描述

下面是程序运行时的截图。本程序模拟这样的一个场景:有两组相互独立的数据需要逐条获取和显示,左边和右边两个groupbox分别代表基于这两组数据的操作,由于他们完全独立,因此可以并行执行。当点击Start按钮,以异步的方式从存储介质中逐条获取数据,并将获取的数据追加到对应的ListBox中,ProgressBar真实反映以获取的数据条数和总记录条数的百分比,同时,当前获取的条数也会在下方的Label上随着操作的继续而动态变化。此外通过点击Stop按钮,可以中止掉当前的操作。当操作被中止后,ProgressBar和Label反映中止的那一刻的状态。

二、代码实现

由于界面上左右两边是两个互不干扰、相互独立的操作,所以分别创建了两个BackgroundWorker组件来负责(如下图:backgroundWorkerLeft和backgroundWorkerRight)。

将两个BackgroundWorker的WorkerReportsProgress和WorkerSupportsCancellation设为true。

我们假设获取的记录数固定,我们为此定义一个常量:

   1: private static int MaxRecords = 100;

下面是左边Start按钮的Click event handler:

   1: private void buttonStartLeft_Click(object sender, EventArgs e)
   2: {
   3:     if (this.backgroundWorkerLeft.IsBusy)
   4:     {
   5:         return;
   6:     }
   7:     this.listBoxLeft.Items.Clear();
   8:     this.backgroundWorkerLeft.RunWorkerAsync(MaxRecords);
   9:     this.buttonStartLeft.Enabled = false;
  10:     this.buttonCacnelLeft.Enabled = true;
  11: } 

当Start按钮被点击后,RunWorkerAsync方法被掉调用,我们定义的常量(MaxRecords )当作参数被掺入。随后,将会触发其DoWork事件,Dowork event handler处理代码如下:

   1: private void backgroundWorkerLeft_DoWork(object sender, DoWorkEventArgs e)
   2: {
   3:     try
   4:     {
   5: e.Result = this.RetrieveData(this.backgroundWorkerLeft, e);
   6:     }
   7:     catch (Exception ex)
   8:     {
   9:        MessageBox.Show(ex.Message);
  10:        throw;
  11:     }
  12: } 

调用RetrieveData方法逐条获取数据。注意该方法的两个参数:BackgroundWorker和DoWorkEventArgs
对象,返回值是返回数据的数量。由于在buttonStartLeft_Click中,我们将常量MaxRecords
作为参数传入了BackgroundWorker的RunWorkerAsync方法, 此时的e.Argument =
MaxRecords。之所以要将这两个参数传入RetrieveData()方法,是因为该方法是为两个BackgroundWorker服务的,需要通过参数来区别当前是哪个BackgroundWorker。我们再来看看RetrieveData方法的定义:

   1: private int RetrieveData(BackgroundWorker worker, DoWorkEventArgs e)
   2: {
   3:     int maxRecords = (int)e.Argument;
   4:     int percent = 0;
   5:     for (int i = 1; i <= maxRecords; i++)
   6:     {
   7:         if (worker.CancellationPending)
   8:         {
   9:             return i;
  10:         } 
  11:  
  12:         percent = (int)(((double)i / (double)maxRecords) * 100);
  13:         worker.ReportProgress(percent, new KeyValuePair<int,string>(i,Guid.NewGuid().ToString()));
  14:         Thread.Sleep(100);
  15:     } 
  16:  
  17:     return maxRecords;
  18: } 

通过e.Argument,获得最大数据获取量之后,进行一个for循环,在每次迭代中,如何worker.CancellationPending==true,代表异步操作被显示取消,则直接返回;否则,调用BackgroundWorker的ReportProgress方法。ReportProgress具有两个重载:

  • public void ReportProgress(int percentProgress);
  • public void ReportProgress(int percentProgress, object userState);

percentProgress代表当前进度,从0-100。userState便于传入一些额外的参数。在界面上,由于数据的当前数量需要实时地显示,而记录也是现取现加(取出一条就在ListBox上追加)。所以制定一个KeyValuePair<int,string>对象作为第二个参数。其中Key为当前记录数,Value是一个Guid,代表取出的数据。

ReportProgress的调用将会导致ProgressChanged事件被触发。ProgressChanged event handler用于显示当前进度、当前记录数量和显示获取的纪录:

   1: private void backgroundWorkerLeft_ProgressChanged(object sender, ProgressChangedEventArgs e)
   2: {
   3:     KeyValuePair<int,string> record = (KeyValuePair<int,string>) e.UserState ;
   4:     this.labelResultLeft.Text = string.Format("There are {0} records retrieved!", record.Key);
   5:     this.progressBarLeft.Value = e.ProgressPercentage;
   6:     this.listBoxLeft.Items.Add(record.Value);
   7: }

注:这些操作需要操作UI上的控件,只能在Main Thread中进行。如何在RetrieveData方法进行的话,由于该方式是一个异步方法,是会抛出异常的。

由于操作的时间可能无法预知,在长时间不能完全获取数据的情况下,用户可以需要手工结束掉当前的操作。这个操作实现在Stop按钮的Click事件中:

   1: private void buttonCacnelLeft_Click(object sender, EventArgs e)
   2: {
   3:     this.backgroundWorkerLeft.CancelAsync();
   4: } 

如何操作正常地结束,BackgroundWorker的RunWorkerCompleted会被触发,下面是RunWorkerCompleted

event handler的定义:

   1: private void backgroundWorkerLeft_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
   2: {
   3:     try
   4:     {
   5:         this.labelResultLeft.Text = string.Format("Total records: {0}", e.Result);
   6:         this.buttonStartLeft.Enabled = true;
   7:         this.buttonCacnelLeft.Enabled = false;
   8:     }
   9:     catch (TargetInvocationException ex)
  10:     {
  11:         MessageBox.Show(ex.InnerException.GetType().ToString());
  12:     }
  13: }

上面介绍的是界面左边功能的实现,右边部分的实现完全一致。干兴趣的朋友可以参考Source Code.

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-08-22 16:00:50

一个通过BackgroundWorker实现WinForm异步操作的例子的相关文章

Linux下一个简单的多线程互斥锁的例子

本篇文章是对Linux下一个简单的多线程互斥锁的例子进行了分析介绍,需要的朋友可以参考下   复制代码 代码如下: #include <stdio.h> #include <pthread.h> pthread_mutex_t Device_mutex ; int count=0; void thread_func1() {    while(1)    {        pthread_mutex_lock(&Device_mutex);        printf(&q

做一个饲养员给动物喂食物的例子体现JAVA中的面向对象思想,接口(抽象类)的用处

做一个饲养员给动物喂食物的例子体现JAVA中的面向对象思想,接口(抽象类)的用处 interface Animal{             //动物的接口    public void eat(Food food); //接口中只有抽象的方法和静态常量!这里void前面省略了abstract.同样,变量可以省略static final} class Cat implements Animal //一种动物类:猫  {      public void eat(Food food) //这里是对

多目标算法中的一个定理怎么理解最好举下例子

问题描述 多目标算法中的一个定理怎么理解最好举下例子 解决方案 感觉很奇怪,没什么逻辑性.

一个超简单的jQuery回调函数例子(分享)_jquery

jQuery回调函数简单使用 比如说,我们想要点击某个按钮后触发事件, 先把一些指定内容给隐藏掉, 然后跳出相关信息的对话框. 如果使用普通的方法, 不用回调函数的话, 会有怎么样的效果呢? 效果是先弹出对话框再隐藏内容, 然后再隐藏指定内容. 这显然不是我们想要的效果, 如果使用回调函数,就可以解决这个问题. 当然,回调函数功能远不只这么简单-- 具体的代码如下: <%@ page language="java" import="java.util.*" p

一个用R语言进行聚类分析的例子

在网上(http://www.rdatamining.com/ )找到了一个用R语言进行聚类分析的例子, 在整个例子中做了一些中文解释说明. 数据集用的是iris. 第一步:对数据集进行初步统计分析 检查数据的维度 > dim(iris) [1] 150   5 显示数据集中的列名 > names(iris) [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Wi

使用BackgroundWorker组件进行异步操作编程

概述 在应用程序中,可能会遇到一些执行耗时的功能操作,比如数据下载.复杂计算及数据库事务等,一般这样的功能会在单独的线程上实现,执行结束后结果显示到用户界面上,这样可避免造成用户界面长时间无响应情况.在.NET 2.0及以后的版本中,FCL提供了BackgroundWorker组件来方便的实现这些功能要求. 组件介绍 BackgroundWorker 类位于System.ComponentModel 命名空间中,通过该类在单独的线程上执行操作实现基于事件的异步模式.下面对BackgroundWo

一个用session传递对象的典型例子,和用面向对象编程的朋友共同分享(转自CSDN)

session|编程|对象 首先,你的session要好使,PHP4.1以上的版本别忘了把php.ini中的register_globle=Off设成register_globle=On,还有就是session.cookie_path = /,注意这行不可以乱改,有的版本PHP改了这个设置session就不好使,这是PHP的一个BUG.好了,我假设你的session已经没问题了,我对下面的例子作个说明:b.php和a.php各定义了一个类,其中a.php中的类a稍微复杂,因为它是由b.php中的

一个简单实现多条件查询的例子

在我们的网站设计过程中,经常会用到多条件查询,本文的源码是一个二手房屋查询的例子.在本例中,我们要实现能够通过地理位置,物业类型,房屋价格,房屋面积及信息发布日期等多个条件查询到客户所需的资料.以下是实现过程. 查询条件界面(略): 查询文件(search.php) 一.生成查询语句: <? $conn=mysql_connect("localhost","root",""); $db=mysql_select_db("lingy

一个简单的Node.js异步操作管理器分享_javascript技巧

最近写nodejs比较多,刚开始的时候碰到的异步的操作比较少,因为想做的东西比较简单,一查api有同步的,为了省事就直接用同步的搞了,慢慢发现这不是个事呀,好好的异步特性不用,非得用同步的,真囧,并且很多东西木有同步的api的. 好!写异步的,慢慢的出现了这种代码... 复制代码 代码如下: mysql.query('xxxx').on('success', function(){   mysql.query('xxxx').on('success', function(){        my