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


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

    image

    这是由于.Net中的大部分控件的实例都是非线程安全的,如果进行跨线程的访问,可能会造成脏数据,所以.Net默认禁止这种跨线程的赋值操作。那要如何解决这个问题呢?

    既然.Net禁止跨线程的赋值操作,那么需要给控件赋值的非UI线程就只有将这种赋值的请求传递给UI线程,最终由UI线程完成赋值的操作。如何传递这种请求?幸运的是,Control类提供了一个Invoke方法。这个方法的功能是将Invoke所指定的方法提交到生成这个Control的UI线程上执行,也就是说我们可以通过调用这个Invoke方法,让UI线程来执行Invoke所指定的方法。 由于Invoke方法是一个public方法,所以所有继承了Control类的.Net控件都可以调用这个方法。

    我们来看一下Invoke方法的声明:

       1: public object Invoke(Delegate method);

    对于这个Invoke方法,它的参数是一个委托类型,调用的时候指向那个你想让UI线程执行的方法,它的返回值是这个委托指定的方法的返回值。那么如果我想让UI线程执行一个带参数的方法怎么办呢?别着急,Invoke还有另外一个重载方法:

       1: public object Invoke(Delegate method, params object[] args);

    这个Invoke方法的参数除了委托类型外,还有一个不定长的参数数组,用来传递委托类型所指定的方法的参数。

    有了这两个Invoke方法后,我们在非UI线程中,如果需要给界面中的控件进行赋值操作的话,就可以调用控件的Invoke方法,让Invoke方法去执行给控件进行赋值的操作,这样一来,实际执行给控件赋值的操作就由UI线程执行了。具体过程请参考下面的例子。

       1: private void button1_Click(object sender, EventArgs e)
       2: {
       3:     label1.Text = "开始了";
       4:     Thread newThread = new Thread(new ThreadStart(DoWork));
       5:     newThread.Start();
       6: }
       7:  
       8: public delegate void SampleDelegate(int i);
       9:  
      10: private void DoWork()
      11: {
      12:     for (int i = 0; i < 10; i++)
      13:     {
      14:         System.Threading.Thread.Sleep(1000);
      15:         if (isCancel)
      16:         {
      17:             isCancel = false;
      18:             return;
      19:         }
      20:         SampleDelegate dele = new SampleDelegate(UpdateLabel);
      21:         label1.Invoke(dele, i);
      22:     }
      23: }
      24:  
      25: private void UpdateLabel(int i)
      26: {
      27:     label1.Text = "当前i的值是:" + i.ToString();
      28: }

    在上面的例子中,我启动了一个新的线程来执行DoWork,然后我在Label1中不停的刷新DoWork方法中的 i 的值,由于DoWork是在非UI线程执行的,这样我不能直接在DoWork中给Label1.Text赋值,所以,我把赋值操作定义到一个方法UpdateLabel中;然后定义一个委托类型(由于Invoke方法的第一个参数是一个委托类型)并实例化它,让这个委托的实例指向UpdateLabel方法;最后我在DoWork中调用label1.Invoke方法,把执行UpdateLabel的委托提交到UI线程进行执行。这样就完成了整个赋值的操作。

    总之,Control.Invoke方法实际上并不是委托的执行者,它仅仅是将委托传递给UI线程,而UI线程才是最终的委托的执行者。所以大家不要被Invoke这个名称所误导,实际上它根本没有执行Invoke操作,仅仅是做了Transfer操作。

    最后,请大家仔细想一想,Invoke操作有没有什么风险?(提示:Control可是还提供了BeginInvoke方法哦~~)

    来源:http://www.cnblogs.com/happinessCodes/archive/2010/07/19/1780964.html

  • 相关阅读:
    转发-》c++ stl multimap基本操作使用技巧详细介绍
    控件传递,待更新
    封装函数获取体的最大4个角
    找vector最大最小《转载》
    获取面面积,资料来自录制和网友分享
    【转】插入排序
    NXOpen获取UFUN的tag
    创建注释
    创建铜公开粗程序
    NXopen create chamfer tool
  • 原文地址:https://www.cnblogs.com/gjhjoy/p/3580526.html
Copyright © 2020-2023  润新知