在.Net中进行跨线程的控件操作(下篇:BackgroundWorker)
在.Net中,如果我们在非UI线程上访问窗体上的控件的时候,会产生一个跨线程调用的异常,那么如何处理这种情况呢?在上一章中,我介绍了使用Control.Invoke方法,如果你不习惯使用委托,那么.Net还为我们提供了一个组件BackgroundWorker,你可以使用这个组件,以事件的方式去处理这种跨线程的控件访问。下面我就来详细的介绍一下这个组件的用法。
我们先来看一下BackgroundWorker提供了哪些常用的成员,
事件
- DoWork:我们在这个事件中,执行需要异步处理的工作。
- ProgressChanged:我们在这个事件中,接收并处理异步处理过程中的信息。
- RunWorkerCompleted:我们在这个事件中,执行异步处理结束的工作。
方法
- RunWorkerAsync()和RunWorkerAsync(object argument):这两个方法触发DoWork事件,开始异步操作。
- ReportProgress(int percentProgress)和ReportProgress(int percentProgress, object userState):这两个方法触发ProgressChanged事件。
- CancelAsync:结束后台的异步操作。
属性
- bool CancellationPending:指示当前的后台的异步操作是否正在被取消,执行CancelAsync方法会导致这个属性为true。
- bool IsBusy:指示当前的后台异步操作是否正在进行,进行中为true。
- bool WorkerReportsProgress:获取或设置当前的BackgroundWorker是否可以执行ProgressChanged方法。
- bool WorkerSupportsCancellation:获取或设置当前的BackgroundWorker是否可以执行CancelAsync方法。
OK,有了上面这些成员,我们来看一下BackgroundWorker是如何工作的。
Step 1. 当然是定义一个BackgroundWorker的实例,你可以从工具箱中拖拽一个BackgroundWorker控件到窗体上或者在代码中直接声明;
Step 2. 生成DoWork事件并在DoWork事件的中添加需要异步执行的代码。在异步执行的代码中,如果需要处理界面中的控件,请调用ReportProgress方法,而不要直接处理(例如给控件赋值),因为DoWork事件跟正常的界面的事件不同,这个事件在非UI线程上执行,所以才能异步执行。
Step 3. 生成ProgressChanged事件并添加控件处理的代码,因为这个事件是在UI线程上执行的,所以可以给界面中的控件进行赋值等操作。
Step 4. 如果需要,请生成RunWorkerCompleted事件,在此处理异步执行结束的业务逻辑。当然,此事件也是在UI线程上执行的,可以给界面中的控件进行赋值等操作。
Step 5. 在需要执行异步操作的地方调用RunWorkerAsync方法,开始执行异步调用。
下面是具体的代码:
1: public Form1()
2: {
3: InitializeComponent();
4: bWorker.DoWork += new DoWorkEventHandler(bWorker_DoWork);
5: bWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bWorker_RunWorkerCompleted);
6: bWorker.ProgressChanged += new ProgressChangedEventHandler(bWorker_ProgressChanged);
7: this.Text = "UI thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString();
8: }
9:
10: BackgroundWorker bWorker = new BackgroundWorker();
11:
12: void bWorker_DoWork(object sender, DoWorkEventArgs e)
13: {
14: int tick = (int)e.Argument;
15: Thread thr = Thread.CurrentThread;
16: for (int i = 0; i < 30; i++)
17: {
18: if (bWorker.CancellationPending)
19: {
20: e.Cancel = true;
21: //break;
22: }
23: else
24: {
25: Thread.Sleep(TimeSpan.FromSeconds(tick));
26: bWorker.ReportProgress(i, DateTime.Now.ToString() + "\\TID:" + thr.ManagedThreadId.ToString());
27: }
28: }
29:
30: }
31:
32: void bWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
33: {
34: progressBar1.Value = e.ProgressPercentage;
35: label1.Text = e.UserState.ToString();
36: }
37:
38: void bWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
39: {
40: label1.Text = DateTime.Now.ToString();
41: progressBar1.Value = progressBar1.Maximum;
42: if (e.Cancelled)
43: label1.Text = "User cancelled.";
44: }
45:
46: private void btnInvoke_Click(object sender, EventArgs e)
47: {
48: bWorker.WorkerReportsProgress = true;
49: bWorker.WorkerSupportsCancellation = true;
50: if (!bWorker.IsBusy)
51: bWorker.RunWorkerAsync(1);
52: }
53:
54: private void btnCancel_Click(object sender, EventArgs e)
55: {
56: if (bWorker.WorkerSupportsCancellation)
57: bWorker.CancelAsync();
58: }
上面的代码请注意几个地方:
1. 第50行,开始调用RunWorkerAsync方法前,请先判断IsBusy属性是否是false,因为如果为true,则说明上一次的调用还没有结束,再次调用会引发异常。
2. 第56行,调用CancelAsync方法前,请先设置WorkerSupportsCancellation属性为true,否则会引发异常。
3. 第26行,调用ReportProgress方法前,请先设置WorkerReportsProgress属性为true,否则会引发异常。
4. RunWorkerAsync方法传递的参数是object类型,这个参数的值可以在DoWork事件的参数e中的属性Argument获得。
5. ReportProgress方法传递的参数可以在事件ProgressChanged中的参数e中获得。
6. 调用CancelAsync方法只是向后台的异步线程发出结束申请,具体什么时候结束,由线程自动管理。
7. 在RunWorkerCompleted事件中,如果想知道后台任务是正常执行完毕还是被调用CancelAsync方法强制中断,请参考事件的参数e的Cancelled属性。(奇怪的是这个属性不会在你调用CancelAsync方法后自动设置为true,你需要象代码中的20行那样进行设置。)
8. 请注意第7行和第26行的代码,这两段代码中的线程的ID,说明了DoWork事件和UI是在两个不同的线程上执行。
实际上BackgroundWorker并非直接用来解决跨线程的控件调用的问题,只是它提供了一种工作机制,可以让你的程序利用它来执行异步调用,并且在异步调用的过程中进行控件的操作。
好了,关于如何对界面中的控件进行跨线程的调用就介绍这么多吧,希望对大家有所帮助。
来源:http://www.cnblogs.com/happinessCodes/archive/2010/07/22/1783199.html
在.Net中进行跨线程的控件操作(下篇:BackgroundWorker)的更多相关文章
- 在.Net中进行跨线程的控件操作(上篇:Control.Invoke)
本文的重点在于介绍如何在多线程编程中,从非UI线程上访问界面中的控件.有过多线程编程经验的人都知道,当我们在非UI线程上试图给一个界面中的控件赋值的时候,比如说label的Text属性,系统会抛出一个 ...
- winform跨线程访问控件
首先说下,.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性.所以除了控件所在的线程外的线程调用会抛异常 (Cross-thread operation not va ...
- C# 跨线程调用控件的4中方法
原文:C# 跨线程调用控件 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应. 同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线 ...
- C# 跨线程调用控件
在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应. 同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...
- C# WinFrom 跨线程访问控件
1.跨线程访问控件委托和类的定义 using System; using System.Windows.Forms; namespace ahwildlife.Utils { /// <summ ...
- C# 关于跨线程访问控件问题
跨线程访问控件问题的原因是:控件都是在主线程中创建的,而系统默认控件的修改权归其创建线程所有.在子线程中如果需要直接修改控件的内容,需要使用委托机制将控件的修改操作交给主线程处理.因此,当没有使用委托 ...
- 【转载】C# 跨线程调用控件
转自:http://www.cnblogs.com/TankXiao/p/3348292.html 感谢原作者,转载以备后用 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停 ...
- c#使用MethodInvoker解决跨线程访问控件
功能函数测试集锦(77) C#专区(114) 版权声明:本文为博主原创文章,未经博主允许不得转载. .net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,有一种方法是禁止编译器对跨线 ...
- 使用TaskScheduler 调度器 实现跨线程的控件访问
//任务调度器 TaskScheduler UIscheduler = null; public Form1() { //获取任务调度器 UIscheduler = TaskScheduler.Fro ...
随机推荐
- curl与wget区别
1.curl是libcurl这个库支持的,wget是一个纯粹的命令行命令.2.curl支持更多的协议.curl supports FTP, FTPS, HTTP, HTTPS, SCP, SFTP, ...
- hibernate之参数绑定
hibernate之参数绑定 ---------- 我们应该拒绝SQL(或HQL)的拼装,应该永远不要编写这样的代码,有这很严重的安全问题,众所周知的SQL注入.我们可以考虑参数绑定,在hiberna ...
- POJ2236 Wireless Network 并查集
水题 #include<cstdio> #include<cstring> #include<queue> #include<set> #include ...
- fcitx中文输入法
Ubuntu自带的输入法不太尽如人意思,用起来也不方便,我在Ubuntu和FC中都是用Fcitx,很好用! 安装配置如下: 1. 安装 fcitx sudo apt-get install fcitx ...
- 【HTML】Beginner1:TagsAttributesElements
HTML(Hypertext Text Mark-up Language)&(How To Make L) HTML is used for meaning and CSS is used f ...
- NOIP2001 Car的旅行路线
题四 Car的旅行路线(30分) 问题描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速 ...
- vijosP1319 数列
vijosP1319 数列 链接:https://vijos.org/p/1319 [思路] 数学. 相当于交换进制2为k. [代码] #include<iostream> using n ...
- HDU1247 - Hat’s Words(Trie树)
题目大意 给定一些单词,要求你把所有的帽子单词找出来,如果某个单词恰好由另外两个单词连接而成,那么它就是帽子单词 题解 先把所有单词插入到Trie树,然后判断每个单词是不是帽子单词,做法就是:对于第i ...
- 问题-Delphi记忆工程打开的单元(XE2设置项)
问题情况:每次在delphi中打开了N个单元的窗口,关闭delphi后,第二天还得一个一个打开单元窗口.问题原因:这是因为delphi的记忆功能未打开.问题处理:Tools->Options.. ...
- (qsf文件 、 tcl文件 和 csv(txt)文件的区别) FPGA管脚分配文件保存、导入导出方法
FPGA管脚分配文件保存方法 使用别人的工程时,有时找不到他的管脚文件,但可以把他已经绑定好的管脚保存下来,输出到文件里. 方法一: 查看引脚绑定情况,quartus -> assignment ...