在.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)的更多相关文章

  1. 在.Net中进行跨线程的控件操作(上篇:Control.Invoke)

    本文的重点在于介绍如何在多线程编程中,从非UI线程上访问界面中的控件.有过多线程编程经验的人都知道,当我们在非UI线程上试图给一个界面中的控件赋值的时候,比如说label的Text属性,系统会抛出一个 ...

  2. winform跨线程访问控件

    首先说下,.net 2.0以后加强了安全机制,不允许在winform中直接跨线程访问控件的属性.所以除了控件所在的线程外的线程调用会抛异常 (Cross-thread operation not va ...

  3. C# 跨线程调用控件的4中方法

    原文:C# 跨线程调用控件 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线 ...

  4. C# 跨线程调用控件

    在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...

  5. C# WinFrom 跨线程访问控件

    1.跨线程访问控件委托和类的定义 using System; using System.Windows.Forms; namespace ahwildlife.Utils { /// <summ ...

  6. C# 关于跨线程访问控件问题

    跨线程访问控件问题的原因是:控件都是在主线程中创建的,而系统默认控件的修改权归其创建线程所有.在子线程中如果需要直接修改控件的内容,需要使用委托机制将控件的修改操作交给主线程处理.因此,当没有使用委托 ...

  7. 【转载】C# 跨线程调用控件

    转自:http://www.cnblogs.com/TankXiao/p/3348292.html 感谢原作者,转载以备后用 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停 ...

  8. c#使用MethodInvoker解决跨线程访问控件

      功能函数测试集锦(77)  C#专区(114)  版权声明:本文为博主原创文章,未经博主允许不得转载. .net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,有一种方法是禁止编译器对跨线 ...

  9. 使用TaskScheduler 调度器 实现跨线程的控件访问

    //任务调度器 TaskScheduler UIscheduler = null; public Form1() { //获取任务调度器 UIscheduler = TaskScheduler.Fro ...

随机推荐

  1. Request Connection: Remote Server @ 192.229.145.200:80

    录制Loadrunner脚本时,提示: Request Connection: Remote Server @ 192.229.145.200:80   NOT INTERCEPTED!(REASON ...

  2. EF架构随心所欲打造属于你自己的DbModel【转】

    前言 我们都知道EF可以生成Dbmodel,系统生成的Model有时候并不是我们想要的,如何我们要生成自己的Model,那么久需要我们手动的去修改T4模版,T4是对“Text Template Tra ...

  3. oracle 创建索引

    一.索引简介 1.索引相当于目录 2.索引是通过一组排序后的索引键来取代默认的全表扫描检索方式,从而提高检索效率. 3.索引的创建要适度,多了会影响增删改的效率,少了会影响查询的效率,索引最好创建在取 ...

  4. 我的iOS开发之路

    我终于开始写我的第一个cocos2d-iphone程序了.纪念一下 额,这是一个悲伤的故事.其实我从开始准备开发iOS已经好久了,从我装上Xcode开始到现在,应该已经有差不多一年的时间了把. 还记得 ...

  5. shell脚本应用(2)--变量,数值和字符串

    变量 定义,赋值: var=value 引用 $var,${var} 特殊变量 $?上条命令状态 $*/$@所有参数列表 $#参数个数 $0执行的命令名称 $1/${10}第1/10个参数 数值运算 ...

  6. CodeForces 352C. Jeff and Rounding(贪心)

    C. Jeff and Rounding time limit per test:  1 second memory limit per test: 256 megabytes input: stan ...

  7. 【三支火把】---C文件学习

    ---恢复内容开始--- 又看了一遍文件的知识点了,断断续续已经看了2-3遍,也就这次花了点时间做了一下总结,以后我想都不会再去翻书了,哈哈. 1. 基于缓冲区的文件操作2. 打开关闭文件3. 单个字 ...

  8. gitservergitlab之搭建和使用

    gitserver比較有名的是gitosis和gitolite,这两个管理和使用起来略微有些复杂,没有web页面,而gitlab则是类似于github的一个工具,github无法免费建立私有仓库,而且 ...

  9. AWS SQS DOC AND RUBY DEMO

    # Amazon SQS 搜集整理aws sqs 的文档以及使用Ruby demo ## Amazon Simple Queue Service (SQS) 是一个可伸缩且可靠的消息传递框架,能够使用 ...

  10. Tomcat 优化

    1.apr 许多朋友可能在启动tomcat的时候都会看到类似这样的信息: 引用 org.apache.catalina.core.AprLifecycleListener init 信息: The A ...