c# Invoke和BeginInvoke 区别分析
control.invoke 方法 (delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
control.begininvoke 方法 (delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
(一)control的invoke和begininvoke
我们要基于以下认识:
(1)control的invoke和begininvoke与delegate的invoke和begininvoke是不同的。
(2)control的invoke和begininvoke的参数为delegate,委托的方法是在control的线程上执行的,也就是我们平时所说的ui线程。
我们以代码(一)来看(control的invoke)
private delegate void invokedelegate(); private void invokemethod(){ //c代码段 } private void butinvoke_click(object sender, eventargs e) { //a代码段....... this.invoke(new invokedelegate(invokemethod)); //b代码段...... }
你觉得代码的执行顺序是什么呢?记好control的invoke和begininvoke都执行在主线程即ui线程上
a------>c---------------->b
解释:(1)a在ui线程上执行完后,开始invoke,invoke是同步
(2)代码段b并不执行,而是立即在ui线程上执行invokemethod方法,即代码段c。
(3)invokemethod方法执行完后,代码段c才在ui线程上继续执行。
看看代码(二),control的begininvoke
private delegate void begininvokedelegate(); private void begininvokemethod(){ //c代码段 } private void butbegininvoke_click(object sender, eventargs e) { //a代码段....... this.begininvoke(new begininvokedelegate(begininvokemethod)); //b代码段...... }
你觉得代码的执行顺序是什么呢?记好control的invoke和begininvoke都执行在主线程即ui线程上
a----------->b--------------->c慎重,这个只做参考。。。。。,我也不肯定执行顺序,如果有哪位达人知道的话请告知。
解释::(1)a在ui线程上执行完后,开始begininvoke,begininvoke是异步
(2)invokemethod方法,即代码段c不会执行,而是立即在ui线程上执行代码段b。
(3)代码段b执行完后(就是说butbegininvoke_click方法执行完后),invokemethod方法,即代码段c才在ui线程上继续执行。
由此,我们知道:
control的invoke和begininvoke的委托方法是在主线程,即ui线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在ui线程上调用control.invoke和control.begininvoke,因为这些是依然阻塞ui线程的,造成界面的假死。
那么,这个异步到底是什么意思呢?
异步是指相对于调用begininvoke的线程异步,而不是相对于ui线程异步,你在ui线程上调用begininvoke ,当然不行了。----摘自"invoke和begininvoke的真正涵义"一文中的评论。
begininvoke的原理是将调用的方法marshal成消息,然后调用win32 api中的registerwindowmessage()向ui窗口发送消息。----摘自"invoke和begininvoke的真正涵义"一文中的评论。
(二)我们用thread来调用begininvoke和invoke
我们开一个线程,让线程执行一些耗费时间的操作,然后再用control.invoke和control.begininvoke回到用户ui线程,执行界面更新。
代码(三) thread调用control的invoke
private thread invokethread; private delegate void invokedelegate(); private void startmethod(){ //c代码段...... control.invoke(new invokedelegate(invokemethod)); //d代码段...... } private void invokemethod(){ //e代码段 } private void butinvoke_click(object sender, eventargs e) { //a代码段....... invokethread = new thread(new threadstart(startmethod)); invokethread.start(); //b代码段...... }
你觉得代码的执行顺序是什么呢?记好control的invoke和begininvoke都执行在主线程即ui线程上
a------>(start一开始b和startmethod的c就同时执行)---->(c执行完了,不管b有没有执行完,invokethread把消息封送(invoke)给ui线程,然后自己等待)---->ui线程处理完butinvoke_click消息后,处理invokethread封送过来的消息,执行invokemethod方法,即代码段e,处理往后ui线程切换到invokethread线程。
这个control.invoke是相对于invokethread线程同步的,阻止了其运行。
解释:
1。ui执行a
2。ui开线程invokethread,b和c同时执行,b执行在线程ui上,c执行在线程invokethread上。
3。invokethread封送消息给ui,然后自己等待,ui处理完消息后,处理invokethread封送的消息,即代码段e
4。ui执行完e后,转到线程invokethread上,invokethread线程执行代码段d
代码(四) thread调用control的begininvoke
private thread begininvokethread; private delegate void begininvokedelegate(); private void startmethod(){ //c代码段...... control.begininvoke(new begininvokedelegate(begininvokemethod)); //d代码段...... } private void begininvokemethod(){ //e代码段 } private void butbegininvoke_click(object sender, eventargs e) { //a代码段....... begininvokethread = new thread(new threadstart(startmethod)); begininvokethread .start(); //b代码段...... }
你觉得代码的执行顺序是什么呢?记好control的invoke和begininvoke都执行在主线程即ui线程上
a在ui线程上执行----->begininvokethread线程开始执行,ui继续执行代码段b,并发地invokethread执行代码段c-------------->不管ui有没有执行完代码段b,这时begininvokethread线程把消息封送给ui,单自己并不等待,继续向下执行-------->ui处理完butbegininvoke_click消息后,处理begininvokethread线程封送过来的消息。
解释:
1。ui执行a
2。ui开线程begininvokethread,b和c同时执行,b执行在线程ui上,c执行在线程begininvokethread上。
3。begininvokethread封送消息给ui,然后自己继续执行代码d,ui处理完消息后,处理invokethread封送的消息,即代码段e
有点疑问:如果ui先执行完毕,是不是有可能过了段时间begininvokethread才把消息封送给ui,然后ui才继续执行封送的消息e。如图浅绿的部分。
control的begininvoke是相对于调用它的线程,即begininvokethread相对是异步的。
因此,我们可以想到。如果要异步取耗费长时间的数据,比如从数据库中读大量数据,我们应该这么做。
(1)如果你想阻止调用线程,那么调用代码(三),代码段d删掉,c改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段e改为更新界面的方法。
(2)如果你不想阻止调用线程,那么调用代码(四),代码段d删掉,c改为耗费长时间的操作,因为这个操作是在另外一个线程中做的。代码段e改为更新界面的方法。