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的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did49014