在C#程序中,经常会有一些耗时较长的CPU密集型运算,如果直接在 UI 线程执行这样的运算就会出现UI不响应的问题。解决这类问题的主要途径是使用多线程,启动一个后台线程,把运算操作放在这个后台线程中完成。但是原生接口的线程操作有一些难度,如果要更进一步的去完成线程间的通信就会难上加难。

还好 .NET 类库中提供了一个叫做 BackgroundWorker 的类可以比较优雅的解决这类问题。虽然BackgroundWorker 类使用起来比较简单,但其中还是有一些需要注意的细节,下面我们就通过 demo 程序介绍它的主要用法。我们在 demo中计算1到100的累加和,为了演示,每次计算都 sleep 600毫秒,demo 的UI为:

用法概述

在窗体上构建一个BackgroundWorker 实例,在它的 DoWork事件处理函数中添加耗时的运算,然后调用它的RunWorkerAsync方法就可以了。

private BackgroundWorker _demoBGWorker = new BackgroundWorker();
_demoBGWorker.DoWork += BGWorker_DoWork;
_demoBGWorker.RunWorkerAsync();
private void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{//在这里执行耗时的运算。int sum = 0;for (int i = 0; i <= 100; i++){sum += i;}
}

是不是有点太简单了?那么让我们考虑下面的问题:

  • 如果我们想要把参数传递给运算过程该怎么做?
  • 在运算过程中我们希望把实时的信息显示在UI上该怎么办?
  • 如果我们想要取消正在进行的运算该怎么办?
  • 如果运算过程出现异常我们又该如何处理?

接下来我们就一个一个的处理这些问题。

把参数传递给运算过程

直接把100写死到运算过程中可不好,我们还打算允许用户指定求和的范围呢!所以需要把100作为参数传递给计算过程。在概述中我们通过调用RunWorkerAsync方法启动计算过程,其实这个方法可以接受一个 object 类型的参数。通过它我们就可以把任何数据传递给计算过程:

//别忘了设置滚动条。
this.progressBarSum.Maximum = 100;
_demoBGWorker.RunWorkerAsync(100);
//下面是更新后的 BGWorker_DoWork 方法:
private void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{//在这里执行耗时的运算。int endNumber = 0;if(e.Argument != null){endNumber = (int)e.Argument;}int sum = 0;for (int i = 0; i <= endNumber; i++){sum += i;}
}

BGWorker_DoWork事件处理函数通过参数 e 的Argument属性传来了我们期望的运算信息。

把消息传递给UI

由于计算过程比较长,我们在通过进度条来显示当前进度的同时,还希望能实时的把计算的中间结果显示在UI上。当然,BackgroundWorker对这个用例也提供了很好的支持。它允许我们在执行计算的过程中给UI线程发送消息,下面看看具体的做法:

_demoBGWorker.WorkerReportsProgress = true;
_demoBGWorker.ProgressChanged += BGWorker_ProgressChanged;

首先要把WorkerReportsProgress 属性设置为 true,然后为ProgressChanged 事件添加处理方法:

private void BGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{//修改进度条的显示。this.progressBarSum.Value = e.ProgressPercentage;//如果有更多的信息需要传递,可以使用 e.UserState 传递一个自定义的类型。//这是一个 object 类型的对象,您可以通过它传递任何类型。//我们仅把当前 sum 的值通过 e.UserState 传回,并通过显示在窗口上。string message = e.UserState.ToString();this.labelSum.Text = message;
}

继续更新 BGWorker_DoWork方法:

private void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{BackgroundWorker bgWorker = sender as BackgroundWorker;int endNumber = 0;if(e.Argument != null){endNumber = (int)e.Argument;}int sum = 0;for (int i = 0; i <= endNumber; i++){sum += i;string message = "Current sum is: " + sum.ToString();//ReportProgress 方法把信息传递给 ProcessChanged 事件处理函数。//第一个参数类型为 int,表示执行进度。//如果有更多的信息需要传递,可以使用 ReportProgress 的第二个参数。//这里我们给第二个参数传进去一条消息。bgWorker.ReportProgress(i, message);Thread.Sleep(600);}
}

OK,现在已经可以看到进度条和执行信息的更新了。

取消操作

在执行过程中允许用户取消当前的操作是一个基本的设计,BackgroundWorker自然有很好的支持:

_demoBGWorker.WorkerSupportsCancellation = true;

和WorkerReportsProgress属性一样,如果要支持取消操作我们需要设置 WorkerSupportsCancellation属性为 true。并且还要在BGWorker_DoWork方法中进行支持,在 for 循环中 Thread.Sleep(600)后面添加代码:

bgWorker.ReportProgress(i, message);
Thread.Sleep(600);//在操作的过程中需要检查用户是否取消了当前的操作。
if (bgWorker.CancellationPending == true)
{e.Cancel = true;break;
}

如果检测到用户点击的取消按钮,就退出当前的计算过程。下面是点击取消按钮时要调用的代码:

_demoBGWorker.CancelAsync();

现在已经可以支持取消操作了,赶快试试吧!

异常处理

如果在计算过程中发生了异常该怎么处理?有没有办法知道计算过程已经结束?当然要有,即便是正常的结束也需要拿到计算的结果。

_demoBGWorker.RunWorkerCompleted += BGWorker_RunWorkerCompleted;
private void BGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{//如果用户取消了当前操作就关闭窗口。if (e.Cancelled){this.Close();}//计算已经结束,需要禁用取消按钮。this.btnCancel.Enabled = false;//计算过程中的异常会被抓住,在这里可以进行处理。if (e.Error != null){Type errorType = e.Error.GetType();switch (errorType.Name){case "ArgumentNullException":case "MyException"://do something.break;default://do something.break;}}//计算结果信息:e.Result//use it do something.
}

RunWorkerCompleted 事件处理函数会在DoWork 事件处理函数返回后被调用。通过它我们可以进行一些运算结束后的操作,比如禁用取消按钮,异常处理,结果显示等。
注意,如果想要拿到 e.Result,您需要在BGWorker_DoWork方法中设置 e.Result属性,如:

e.Result = sum;

总结,BackgroundWorker 类功能完善且使用简便,实在是处理异步耗时操作的利器!

c#学习笔记---BackgroundWorker 详解相关推荐

  1. IOS开发学习笔记-----UILabel 详解

    IOS开发学习笔记-----UILabel 详解 01 //创建uilabel 02 UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMa ...

  2. wringPi 初始化GPIO 为上拉_敏矽微电子Cortex-M0学习笔记04-GPIO详解及应用实例

    前面我们已经对敏矽微电子的基于cortex m0内核的ME32F030R8T6的基本功能做了介绍,然后详细讲解了开发环境MDK的安装,pack包的安装,工程的建立及程序的仿真,紧接着讲解了ME32F0 ...

  3. python学习笔记 正则表达式 详解2

    python学习笔记 正则表达式 详解 行定位符 行定位符就是用来描述子串的边界."^"表示行的开始:"$"表示行的结尾 ^tm:匹配以子串tm的开始位置是行头 ...

  4. 关于ClassLoader的学习笔记,详解版

    ClassLoader 详解 ClassLoader 做什么的? 延迟加载 各司其职 ClassLoader 传递性 双亲委派 Class.forName 自定义加载器 Class.forName v ...

  5. DNS 学习笔记之三- 详解DNS的资源记录

    <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />       最 ...

  6. JavaWeb学习笔记——JSON详解

    本文是学习Java时所记录的学习笔记,本节记录了JSON的相关知识,需要掌握JSON的书写格式,懂得JSON与String类型的转换. 文章目录 JSON简介 什么是JSON? JSON语法规则 JS ...

  7. ctfhub技能书+历年真题学习笔记(详解)

    Web Web前置技能 HTTP协议 请求方式 题目:HTTP Method is GET Use CTF**B Method, I will give you flag. Hint: If you ...

  8. Linux实操篇——实用指令学习笔记(详解)

    9.3帮助指令 9.3.1介绍 当我们对某个指令不熟悉时,我们可以使用Linux提供的帮助指令来了解这个指令的使用方法. 9.3.2man 获得帮助信息 基本语法 man[命令或配置文件](功能描述: ...

  9. 十一、MySQL视图学习笔记(详解)

    视图 含义:理解成一张虚拟的表,和普通表一样使用 mysql5.1版本出现的新特性,是通过表动态生成的数据 比如:舞蹈班和普通班级的对比 视图和表的区别: 名称 创建语法的关键字 使用 占用物理空间 ...

  10. 进厂手册:Git 学习笔记(详解命令)

    文章目录 git 对象 通过git对象进行文件的保存 git对象的缺点 树对象 构建树对象 提交对象 高层命令 工作区的文件状态 git reset hard 咋用以及用错了怎么恢复 git chec ...

最新文章

  1. 【TCP/IP详解 卷一:协议】第十九章 TCP的交互数据流
  2. 【黑客浅析】像黑客一样思考
  3. Linux shell编程(四)流程控制
  4. python 同时给多个变量赋值
  5. python arcade库是干什么的-Python街机模块arcade的鼠标移动与单击示例
  6. 独立测试团队在敏捷开发中的几个特别实践
  7. 彻底掌握机器学习的6个主流模型,是什么水平?
  8. FreeRTOS 低功耗之 tickless 模式
  9. php连接数据库输出的中文几个字就…
  10. 功放音量调节原理_玩汽车音响,功放和喇叭,应该如何做好匹配?
  11. linux centos7.9图形界面版本下载_适合在任何地方使用的 Linux:15 个小型 Linux 发行版...
  12. 【java编程规范】阿里巴巴编程考试规范+真题答案+考试分享
  13. Adobe Creative Cloud 2022 (macOS、Windows) TNT 合集
  14. 学习微信小程序的资料汇总---转载自知乎
  15. 利用华为手机给台式机提供网络
  16. 使用OpenCV测量图像中物体之间的距离
  17. 阿里的防DDoS能力有多强,小蚁带你了解一下
  18. MAC版本subline text快捷键大全
  19. 假如生产环境出现CPU占用过高,如何分析思路和定位?
  20. 打印出一个数的每一位

热门文章

  1. 异步社区两周年 - 技术图书免费送(活动已结束)
  2. unity3d 台球源码_unity3D台球游戏源码
  3. 使用二维数组打印一个 10 行杨辉三角
  4. cdrx8如何批量导出jpg_Coreldraw/CDR X8 存低版本打开问题 – 数码打印破图 – Coreldraw/CDR软件崩溃 – 渐变导位图角度变了...
  5. 树莓派4b--红外遥控模块
  6. AllenNLP框架学习笔记(入门篇)
  7. 论开学第二个月干了点啥
  8. 手把手教你写CSS行内样式与内联样式
  9. LayUI表单验证select定位失效问题
  10. arcgis重分类读不出值的解决办法?