好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Invoke和BeginInvoke

Invoke和BeginInvoke

今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别。所以花了点时间研究了下。

  据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的。

Control. Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。 Control.BeginInvoke 方法 ( Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
 msdn说明:

控件上的大多数方法只能从创建控件的线程调用。   如果已经创建控件的句柄,则除了  InvokeRequired  属性以外,控件上还有四个可以从任何线程上安全调用的方法,它们是: Invoke 、 BeginInvoke 、 EndInvoke  和  CreateGraphics 。   在后台线程上创建控件的句柄之前调用 CreateGraphics  可能会导致非法的跨线程调用。   对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。   调用方法始终在控件的线程上调用自己的回调。

  

  于是用下面的代码进行初步的测试:  

   1.主线程调用Invoke    

  1           ///   <summary> 
  2           ///   直接调用Invoke
   3           ///   </summary> 
  4           private   void   TestInvoke()
   5           {
   6              listBox1.Items.Add( "  --begin--  "  );
   7              listBox1.Invoke( new  Action(() =>
  8               {
   9                  listBox1.Items.Add( "  Invoke  "  );
  10               }));
  11  
 12              Thread.Sleep( 1000  );
  13              listBox1.Items.Add( "  --end--  "  );
  14          }

输出:    

  

  从输出结果上可以看出,Invoke被调用后,是马上执行的。这点很好理解。

   2.主线程调用BeginInvoke

  1           ///   <summary> 
  2           ///   直接调用BeginInvoke
   3           ///   </summary> 
  4           private   void   TestBeginInvoke()
   5           {
   6              listBox1.Items.Add( "  --begin--  "  );
   7               var  bi = listBox1.BeginInvoke( new  Action(() =>
  8               {
   9                   //  Thread.Sleep(10000); 
 10                  listBox1.Items.Add( "  BeginInvoke  "  );
  11               }));
  12              Thread.Sleep( 1000  );
  13              listBox1.Items.Add( "  --end--  "  );
  14          }

输出:  

  

  从输出能看出,只有当调用BeginInvoke的线程结束后,才执行它的内容。

  不过有两种情况下,它会马上执行:

   使用EndInvoke,检索由传递的  IAsyncResult  表示的异步操作的返回值。

         ///   <summary> 
         ///   调用BeginInvoke、EndInvoke
          ///   </summary> 
         private   void   TestBeginInvokeEndInvoke()
        {
            listBox1.Items.Add(  "  --begin--  "  );
              var  bi = listBox1.BeginInvoke( new  Action(() => 
            {
                Thread.Sleep(  1000  );
                listBox1.Items.Add(  "  BeginInvokeEndInvoke  "  );
            }));
            listBox1.EndInvoke(bi);
            listBox1.Items.Add(  "  --end--  "  );
        } 

输出:  

  

   

   同一个控件调用Invoke时,会马上执行先前的BeginInvoke

         ///   <summary> 
         ///   调用BeginInvoke、Invoke
          ///   </summary> 
         private   void   TestBeginInvokeInvoke()
        {
            listBox1.Items.Add(  "  --begin--  "  );
            listBox1.BeginInvoke(  new  Action(() => 
                {
                    Thread.Sleep(  1000  );
                    listBox1.Items.Add(  "  BeginInvoke  "  );
                }));
            listBox1.Invoke(  new  Action(() => 
                {
                    listBox1.Items.Add(  "  Invoke  "  );
                }));
            listBox1.Items.Add(  "  --end--  "  );
        } 

输出:

  

   注:在主线程中直接调用Invoke、BeginInvoke、EndInvoke都会造成阻塞。所以应该利用副线程(支线线程)调用。

   3.支线线程调用Invoke

   创建一个线程,并在线程中调用Invoke,同时测试程序是在哪个线程中调用Invoke。 

  1           ///   <summary> 
  2           ///   线程调用Invoke
   3           ///   </summary> 
  4           private   void   ThreadInvoke()
   5           {
   6              listBox1.Items.Add( "  --begin--  "  );
   7               new  Thread(() =>
  8               {
   9                  Thread.CurrentThread.Name =  "  ThreadInvoke  "  ;
  10                  listBox1.Invoke( new  Action(() =>
 11                       {
  12                          Thread.Sleep( 10000  );
  13                           this .listBox1.Items.Add( "  ThreadInvoke:  "  +  Thread.CurrentThread.Name);                   
  14                       }));
  15               }).Start();
  16              Thread.Sleep( 1000  );
  17              listBox1.Items.Add( "  --end--  "  );
  18           }
  19  
 20  
 21           private   void  button1_Click( object   sender, EventArgs e)
  22           {
  23              Thread.CurrentThread.Name =  "  Main  "  ;
  24               ThreadInvoke();
  25           }
  26  
 27           private   void  button2_Click( object   sender, EventArgs e)
  28           {
  29              listBox1.Items.Add( "  button2_Click  "  );
  30          }

输出:

  

  当点击button1后,我试图去点击button2,却发现程序被阻塞了。可见Invoke尽管在支线程中调用,实际上仍然在 拥有此控件的基础窗口句柄的线程上执行。

   

   接着来测试下在支线程中调用BeginInvoke.

   4.支线线程调用BeginInvoke

  1           ///   <summary> 
  2           ///   线程调用BeginInvoke
   3           ///   </summary> 
  4           private   void   ThreadBeginInvoke()
   5           {
   6              listBox1.Items.Add( "  --begin--  "  );
   7               new  Thread(() =>
  8               {
   9                  Thread.CurrentThread.Name =  "  ThreadBeginInvoke  "  ;
  10                  Thread.Sleep( 10000  );
  11                   string  temp =  "  Before!  "  ;
  12                  listBox1.BeginInvoke( new  Action(() =>
 13                   {
  14                       this .listBox1.Items.Add(temp +  "  :  "  +  Thread.CurrentThread.Name);
  15                   }));
  16                  temp +=  "  After!  "  ;
  17               }).Start();
  18              Thread.Sleep( 1000  );
  19              listBox1.Items.Add( "  --end--  "  );
  20           }
  21  
 22  
 23           private   void  button1_Click( object   sender, EventArgs e)
  24           {
  25              Thread.CurrentThread.Name =  "  Main  "  ;
  26               ThreadBeginInvoke();
  27           }
  28  
 29           private   void  button2_Click( object   sender, EventArgs e)
  30           {
  31              listBox1.Items.Add( "  button2_Click  "  );
  32          }

输出:  

  

  从这结果中我们能得出以下几点:

线程真正开始执行,是在创建它的线程结束后执行。 真正执行BeginInvoke的线程就是创建控件线程上。 BeginInvoke在线程中调用时,是异步执行的而且没有对主线程造成阻塞 。(button2_Click在Before!After!:Main前面) BeginInvoke只有当创建它的线程结束后执行 。(当然你也能通过EndInvoke、Invoke让其提早执行。)

总结:   

  以下为了方便理解,假设如下:

    主线程表示Control.Invoke或Control.BeginInvoke中Control所在的线程,即创建该创建的线程。(一般为UI线程)

    支线程表示调用Invoke或BeginInvoke的线程。

Invoke、BeginInvoke始终在主线程中执行。 Invoke被调用时就会直接执行,也就是直接阻塞线程(包括主支线程),直到它结束。而BeginInvoke只有等支线程结束或者调用EndInvoke、Invoke时才会开始执行。 Invoke不管在哪里执行都会造成主线程的阻塞。而BeginInvoke只会阻塞支线程,而对于主线程是异步执行。(注意,如果在主线程中调用,也会阻塞主线程)。 在支线程中,应该使用BeginInvoke,否则调用Invoke将导致支线程阻塞主线程,该支线程就没有存在的意义。(当然有特殊需求除外)

                                       SamWang

                                       2012-05-25

作者: SamWang  
出处: http://wangshenhe.cnblogs.com/  
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Invoke和BeginInvoke的详细内容...

  阅读:39次

上一篇: Node.js 技術

下一篇:WCF 开发学习笔记