原文:BackgroundWorker 简单使用教程 多个线程的创建

BackgroundWorker是一个非常不错的线程控件,能避免界面假死,让线程操作你想要做的事,它学习起来很简单,但是能实现很强大的功能。发布这篇文章的目的是将最近学习到的共享出来,大家交流一下,当然我也是菜鸟,在这里你将学习到BackgroundWorker简单使用,停止,暂停,继续等操作,BackgroundWorker比起Thread和ThreadPool要简单太多,为了更方便在实际应用中使用,我使用的是winform,没有使用控制台程序。

在UI界面里拖动一个button和richTextBox到界面。

我会从最简单的开始,只有最简单的代码才会让人有继续学下去的欲望,下列代码可以将1到999打印到richTextBox1控件上。

 1 private void button1_Click(object sender, EventArgs e)
 2 {
 3     //创建一个BackgroundWorker线程
 4     BackgroundWorker bw = new BackgroundWorker();
 5     //创建一个DoWork事件,指定bw_DoWork方法去做事
 6     bw.DoWork += new DoWorkEventHandler(bw_DoWork);
 7     //开始执行
 8     bw.RunWorkerAsync();
 9 }
10
11 void bw_DoWork(object sender, DoWorkEventArgs e)
12 {
13     for (int i = 0; i < 1000; i++)
14     {
15         this.richTextBox1.Text += i + Environment.NewLine;
16     }
17 }

但是很不幸,以上代码会报错,报错信息:线程间操作无效: 从不是创建控件“richTextBox1”的线程访问它。

那么我们继续改造代码,让数字显示在richTextBox1控件上,并且让richTextBox1焦点处于最低端。

 1 private void button1_Click(object sender, EventArgs e)
 2 {
 3     //创建一个BackgroundWorker线程
 4     BackgroundWorker bw = new BackgroundWorker();
 5     //创建一个DoWork事件,指定bw_DoWork方法去做事
 6     bw.DoWork += new DoWorkEventHandler(bw_DoWork);
 7     //开始执行
 8     bw.RunWorkerAsync();
 9 }
10
11 void bw_DoWork(object sender, DoWorkEventArgs e)
12 {
13     for (int i = 0; i < 1000; i++)
14     {
15         this.Invoke((MethodInvoker)delegate
16         {
17             this.richTextBox1.Text += i + Environment.NewLine;
18         });
19     }
20 }
21
22 private void richTextBox1_TextChanged(object sender, EventArgs e)
23 {
24     RichTextBox textbox = (RichTextBox)sender;
25
26     textbox.SelectionStart = textbox.Text.Length;
27     textbox.ScrollToCaret();
28 }

上面是BackgroundWorker一个最简单的例子,没有多余复杂的代码,这就是BackgroundWorker,下面我们加入停止按钮,让线程停下来。

再拖动一个button控件到界面,让线程停止我们先要改造一下代码,让button事件也能控制到BackgroundWorker线程。

 1 BackgroundWorker bw = null;
 2
 3 private void button1_Click(object sender, EventArgs e)
 4 {
 5     //创建一个BackgroundWorker线程
 6     bw = new BackgroundWorker();
 7     //指定可以让线程停止
 8     bw.WorkerSupportsCancellation = true;
 9     //创建一个DoWork事件,指定bw_DoWork方法去做事
10     bw.DoWork += new DoWorkEventHandler(bw_DoWork);
11     //开始执行
12     bw.RunWorkerAsync();
13 }
14
15 private void button2_Click(object sender, EventArgs e)
16 {
17     //停止线程
18     bw.CancelAsync();
19 }
20
21 void bw_DoWork(object sender, DoWorkEventArgs e)
22 {
23     for (int i = 0; i < 1000; i++)
24     {
25         //获取当前线程是否得到停止的指令
26         if (bw.CancellationPending)
27         {
28             e.Cancel = true;
29             return;
30         }
31
32         this.Invoke((MethodInvoker)delegate
33         {
34             this.richTextBox1.Text += i + Environment.NewLine;
35         });
36     }
37 }

为了避免代码的复杂化,上面代码我没有做更多的体验修改,比如点击开始的按钮,开始的按钮应该为不可用状态,点击停止按钮后停止按钮不可用状态,激活开始按钮。

下面我们将继续升级,如何来获知线程是否已经执行完成或者线程已经停止了呢

 1 BackgroundWorker bw = null;
 2
 3 private void button1_Click(object sender, EventArgs e)
 4 {
 5     bw = new BackgroundWorker();
 6     bw.WorkerSupportsCancellation = true;
 7     bw.DoWork += new DoWorkEventHandler(bw_DoWork);
 8     //线程完成或者停止发生的事件
 9     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
10
11     bw.RunWorkerAsync();
12 }
13
14 private void button2_Click(object sender, EventArgs e)
15 {
16     bw.CancelAsync();
17 }
18
19 void bw_DoWork(object sender, DoWorkEventArgs e)
20 {
21     for (int i = 0; i < 1000; i++)
22     {
23         if (bw.CancellationPending)
24         {
25             e.Cancel = true;
26             return;
27         }
28
29         this.Invoke((MethodInvoker)delegate
30         {
31             this.richTextBox1.Text += i + Environment.NewLine;
32         });
33     }
34 }
35
36 void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
37 {
38     if (e.Cancelled)
39     {
40         this.richTextBox1.Text += "线程已经停止";
41     }
42     else
43     {
44         this.richTextBox1.Text += "线程已经完成";
45     }
46 }

到现在为止你可以自己去用BackgroundWorker创建一个线程了,你已经了解它了,当然BackgroundWorker还有一个ReportProgress滚动条事件,可以显示进度,我暂且认为它是多余的,因为大部分进度都可以通过bw_DoWork来控制实现。下面我们继续完善BackgroundWorker,加入暂停和继续功能。

再拖动一个button控件到界面,BackgroundWorker的暂停和继续我们使用ManualResetEvent。

 1 BackgroundWorker bw = null;
 2 //创建ManualResetEvent
 3 ManualResetEvent mr = new ManualResetEvent(true);
 4
 5 private void button1_Click(object sender, EventArgs e)
 6 {
 7     bw = new BackgroundWorker();
 8     bw.WorkerSupportsCancellation = true;
 9     bw.DoWork += new DoWorkEventHandler(bw_DoWork);
10     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
11
12     bw.RunWorkerAsync();
13 }
14
15 private void button2_Click(object sender, EventArgs e)
16 {
17     bw.CancelAsync();
18 }
19
20 private void button3_Click(object sender, EventArgs e)
21 {
22     Button b = (Button)sender;
23     if (b.Text == "暂停")
24     {
25         mr.Reset();
26         b.Text = "继续";
27     }
28     else
29     {
30         mr.Set();
31         b.Text = "暂停";
32     }
33
34 }
35
36 void bw_DoWork(object sender, DoWorkEventArgs e)
37 {
38     for (int i = 0; i < 1000; i++)
39     {
40         if (bw.CancellationPending)
41         {
42             e.Cancel = true;
43             return;
44         }
45
46         this.Invoke((MethodInvoker)delegate
47         {
48             this.richTextBox1.Text += i + Environment.NewLine;
49         });
50
51         //接受指令
52         mr.WaitOne();
53     }
54 }
55
56 void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
57 {
58     if (e.Cancelled)
59     {
60         this.richTextBox1.Text += "线程已经停止";
61     }
62     else
63     {
64         this.richTextBox1.Text += "线程已经完成";
65     }
66 }

到目前为止BackgroundWorker的大部分功能都实现了,上面的代码在很多博客中都能找到,都是只执行了一个后台线程。如果我们有1千个耗时的任务,那么一个线程远远不够,我们需要创建多条线程,让他分段执行,比如创建10个线程,把1千个任务分成不同的等分让10个线程分别去执行。

我们使用list泛型 List<BackgroundWorker>,然后使用bw.RunWorkerAsync(i) 传递参数到bw_DoWork里,在bw_DoWork里使用e.Argument接受参数。

 1 List<BackgroundWorker> bws = new List<BackgroundWorker>();
 2 int t = 10;
 3
 4 private void button1_Click(object sender, EventArgs e)
 5 {
 6     for (int i = 0; i < t; i++)
 7     {
 8         BackgroundWorker bw = new BackgroundWorker();
 9         bw.DoWork += new DoWorkEventHandler(bw_DoWork);
10         bws.Add(bw);
11
12         bw.RunWorkerAsync(i);
13     }
14 }
15
16 void bw_DoWork(object sender, DoWorkEventArgs e)
17 {
18     int j = Convert.ToInt32(e.Argument);
19     for (int i = j; i < 1000; i = i + t)
20     {
21         if (((BackgroundWorker)sender).CancellationPending)
22         {
23             e.Cancel = true;
24             return;
25         }
26
27         string item = String.Format("线程{0}正在操作数据{1}", j + 1, i);
28
29         this.Invoke((MethodInvoker)delegate
30         {
31             this.richTextBox1.Text += item + Environment.NewLine;
32         });
33
34         //Thread.Sleep(200);
35     }
36 }

由于上面代码不是耗时操作,又开启线程10个,操作过快,造成界面假死状态,可以使用Sleep让线程休眠。

我们继续完善代码,加入停止操作,加入完成后和停止的事件,由于是多线程,判断是线程操作是否完成,我们用bws.Remove(sender as BackgroundWorker); 方法删除线程,然后使用bws.Count == 0来判断是否操作完成。

 1 List<BackgroundWorker> bws = new List<BackgroundWorker>();
 2 int t = 10;
 3
 4 private void button1_Click(object sender, EventArgs e)
 5 {
 6     for (int i = 0; i < t; i++)
 7     {
 8         BackgroundWorker bw = new BackgroundWorker();
 9         bw.DoWork += new DoWorkEventHandler(bw_DoWork);
10         bw.WorkerSupportsCancellation = true;
11         bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
12         bws.Add(bw);
13
14         bw.RunWorkerAsync(i);
15     }
16 }
17
18 private void button2_Click(object sender, EventArgs e)
19 {
20     for (int i = 0; i < t; i++)
21     {
22         bws[i].CancelAsync();
23     }
24 }
25
26 void bw_DoWork(object sender, DoWorkEventArgs e)
27 {
28     int j = Convert.ToInt32(e.Argument);
29     for (int i = j; i < 1000; i = i + t)
30     {
31         if (((BackgroundWorker)sender).CancellationPending)
32         {
33             e.Cancel = true;
34             return;
35         }
36
37         string item = String.Format("线程{0}正在操作数据{1}", j + 1, i);
38
39         this.Invoke((MethodInvoker)delegate
40         {
41             this.richTextBox1.Text += item + Environment.NewLine;
42         });
43
44         Thread.Sleep(200);
45     }
46 }
47
48 void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
49 {
50     bws.Remove(sender as BackgroundWorker);
51     if (bws.Count == 0)
52     {
53         if (e.Cancelled)
54         {
55             this.richTextBox1.Text += "线程已经停止";
56         }
57         else
58         {
59             this.richTextBox1.Text += "线程已经完成";
60         }
61     }
62 }

上面代码中的停止不是能立即停止,这个就和开车一样,开的越快,刹车的后拖行的距离越长,同理,开启的线程的越多,完全暂停需要的时间越长,不知我说的是否正确。另外我也想问一下是否能真正的全部线程停止,点停止后全部线程立即停止。

下面我们继续加入暂停和继续的功能,一样的道理,我们使用List<ManualResetEvent>。

 1 List<BackgroundWorker> bws = new List<BackgroundWorker>();
 2 List<ManualResetEvent> mrs = new List<ManualResetEvent>();
 3 int t = 10;
 4
 5 private void button1_Click(object sender, EventArgs e)
 6 {
 7     for (int i = 0; i < t; i++)
 8     {
 9         BackgroundWorker bw = new BackgroundWorker();
10         bw.DoWork += new DoWorkEventHandler(bw_DoWork);
11         bw.WorkerSupportsCancellation = true;
12         bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
13         bws.Add(bw);
14
15         bw.RunWorkerAsync(i);
16
17         mrs.Add(new ManualResetEvent(true));
18     }
19 }
20
21 private void button2_Click(object sender, EventArgs e)
22 {
23     for (int i = 0; i < t; i++)
24     {
25         bws[i].CancelAsync();
26     }
27 }
28
29 private void button3_Click(object sender, EventArgs e)
30 {
31     Button b = (Button)sender;
32     if (b.Text == "暂停")
33     {
34         for (int i = 0; i < mrs.Count; i++)
35         {
36             mrs[i].Reset();
37         }
38         b.Text = "继续";
39     }
40     else
41     {
42         for (int i = 0; i < mrs.Count; i++)
43         {
44             mrs[i].Set();
45         }
46         b.Text = "暂停";
47     }
48 }
49
50 void bw_DoWork(object sender, DoWorkEventArgs e)
51 {
52     int j = Convert.ToInt32(e.Argument);
53     for (int i = j; i < 1000; i = i + t)
54     {
55         if (((BackgroundWorker)sender).CancellationPending)
56         {
57             e.Cancel = true;
58             return;
59         }
60
61         string item = String.Format("线程{0}正在操作数据{1}", j + 1, i);
62
63         this.Invoke((MethodInvoker)delegate
64         {
65             this.richTextBox1.Text += item + Environment.NewLine;
66         });
67
68         Thread.Sleep(200);
69         mrs[j].WaitOne();
70     }
71 }
72
73 void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
74 {
75     bws.Remove(sender as BackgroundWorker);
76     if (bws.Count == 0)
77     {
78         if (e.Cancelled)
79         {
80             this.richTextBox1.Text += "线程已经停止";
81         }
82         else
83         {
84             this.richTextBox1.Text += "线程已经完成";
85         }
86     }
87 }

至此,所有的代码都奉上了,多个线程操作会带来很多意向不到的麻烦,比如多个线程同时把数据写入一个文件,多线程更新datatable等,会让软件莫名其妙的自动退出,.net2.0里还没有绝对线程安全的数据集,很多大佬都说用lock,但我对lock也是一知半解,还请大家赐教赐教,如上有什么说的不对,也请大家多多指点。

BackgroundWorker 简单使用教程 多个线程的创建相关推荐

  1. jvisualvm oracle,Jvisualvm简单使用教程

    本博客介绍一下jvisualvm的简单使用教程,jvisualvm功能还是挺多的,不过本博客之简单介绍一下 1.拿线程快照信息 在jdk安装目录找到jvisualvm.exe,${JDK_HOME}\ ...

  2. 一个简单的例子看java线程机制

    一个简单的例子看java线程机制 作者: zyf0808 发表日期: 2006-03-26 11:20 文章属性: 原创 复制链接 import java.util.*; public class T ...

  3. vim简单使用教程【转】

    vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会对这个编辑器失去兴趣的.下面的文章翻译自<Learn Vim Progress ...

  4. java map set_java中Map、Set、List的简单使用教程(快速入门)

    Map.Set.List List的常用方法 1.创建 List list = new ArrayList<>(); List list = new LinkedList<>( ...

  5. 【转】正则表达式简介及在C++11中的简单使用教程

    [转]正则表达式简介及在C++11中的简单使用教程 正则表达式Regex(regular expression)是一种强大的描述字符序列的工具.在许多语言中都存在着正则表达式,C++11中也将正则表达 ...

  6. PHP在WPS中的应用,PHP+Laravel的简单应用教程【ajax的使用】,wps的使用教程

    PHP+Laravel的简单应用教程[ajax的使用]PHP·拉弗尔的简单应用教程[阿贾克斯的使用],下面由Laravel框架教程栏目给大家介绍PHP Laravel的简单应用教程[阿贾克斯的使用], ...

  7. java最少有多少线程_【并发编程】一个最简单的Java程序有多少线程?

    一个最简单的Java程序有多少线程? 通过下面程序可以计算出当前程序的线程总数. import java.lang.management.ManagementFactory; import java. ...

  8. 史上最简单MySQL教程详解(进阶篇)之存储过程(一)

    史上最简单MySQL教程详解(进阶篇)之存储过程(一) 史上最简单MySQL教程详解(进阶篇)之存储过程(一) 什么是存储过程 存储过程的作用 如何使用存储过程 创建存储过程 DELIMITER改变分 ...

  9. 史上最简单MySQL教程详解(进阶篇)之存储引擎介绍及默认引擎设置

    什么是存储引擎? MySQL存储引擎种类 MyISAM 引擎 InnoDB引擎 存储引擎操作 查看存储引擎 存储引擎的变更 修改默认引擎 什么是存储引擎? 与其他数据库例如Oracle 和SQL Se ...

  10. IDEA简单配置教程

    IDEA简单配置教程 --做好前期配置工作,后期少走弯路. 创建模块(Module) 设置(Settings) 设置主题 窗体及菜单字体及大小 设置编辑区主题 通过插件(plugins)更换主题 设置 ...

最新文章

  1. 迷宫问题输出超限反思
  2. centos mysql压缩文件直接恢复_Centos下mysql数据库备份与恢复的方法
  3. 【收藏】ABAP Bom按层展开的几种实现方法
  4. librosa能量_librosa与python_speech_features
  5. native.loadlibrary获取路径不对_【Python专题(三)】Python模块导入与路径管理
  6. msp430单片机 温度计编程_MSP430系列与89C5l系列的比较
  7. vue中watch的用法总结以及报错处理Error in callback for watcher checkList
  8. [220207] Find the Difference
  9. 单片机STM32低功耗
  10. 最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架
  11. i2c我们用得很多,i3c又是什么?
  12. hazelcast 搭建_hazelcast Management Center 源码分析
  13. win7打不开计算机网络连接,win7系统有网络但是打不开网页的解决方法【图文】...
  14. 中央气象局天气预报城市对应的编码(到县为止)
  15. python开发基础之数据类型、字符编码、文件操作
  16. K8S官方部署方式以及自签SSL证书介绍
  17. 【06月19日】A股滚动市盈率PE最低排名
  18. 香港5G自动驾驶车开测,明年大规模推出5G服务
  19. 冀教版五年级计算机教学计划,冀教版信息技术五年级下册教学计划.doc
  20. 200A FS3L200R10W3S7FB11 EasyPACK 950V IGBT模块

热门文章

  1. python计算工资_python学习之工资结算
  2. xtragrid 某个值 查找_EXCEL查找引用函数:如果只推荐一个EXCEL函数,那就是这个了...
  3. java虚拟机的gc机制的优缺点_深入Java虚拟机之 -- 总结面试篇
  4. java 把数据写入xml_java中用jdom 如何创建xml文档/将数据写入XML中
  5. 理解Spring MVC Model Attribute 和 Session Attribute
  6. IBM DB2关键特性解析:DB2分区特性
  7. cocostudio学习(UIScrollView控件内部子控件的可视化问题)
  8. Bootstrap3 表单-水平排列的表单
  9. 查看oracle数据库是否归档和修改归档模式(转)
  10. 高中计算机评课,高二信息技术评课记录