收藏 分享(赏)

invoke 和 begininvoke 的区别.doc

上传人:天天快乐 文档编号:1144846 上传时间:2018-06-15 格式:DOC 页数:7 大小:79KB
下载 相关 举报
invoke 和 begininvoke 的区别.doc_第1页
第1页 / 共7页
invoke 和 begininvoke 的区别.doc_第2页
第2页 / 共7页
invoke 和 begininvoke 的区别.doc_第3页
第3页 / 共7页
invoke 和 begininvoke 的区别.doc_第4页
第4页 / 共7页
invoke 和 begininvoke 的区别.doc_第5页
第5页 / 共7页
点击查看更多>>
资源描述

1、Invoke 和 BeginInvoke 的区别在 Invoke 或者 BeginInvoke 的使用中无一例外地使用了委托 Delegate。 一、为什么 Control 类提供了 Invoke 和 BeginInvoke 机制?关于这个问题的最主要的原因已经是 dotnet 程序员众所周知的,我在此费点笔墨再次记录到自己的日志,以便日后提醒一下自己。1、windows 程序消息机制Windows GUI 程序是基于消息机制的,有个主线程维护着一个消息泵。这个消息泵让 windows 程序生生不息。Windows GUI 程序的消息循环Windows 程序有个消息队列,窗体上的所有消息是这个

2、队列里面消息的最主要来源。这里的 while 循环使用了 GetMessage()这个方法,这是个阻塞方法,也就是队列为空时方法就会被阻塞,从而这个 while 循环停止运动,这避免了一个程序把 cpu 无缘无故地耗尽,让其它程序难以得到响应。当然在某些需要cpu 最大限度运动的程序里面就可以使用另外的方法,例如某些3d 游戏或者及时战略游戏中,一般会使用 PeekMessage()这个方法,它不会被 windows 阻塞,从而保证整个游戏的流畅和比较高的帧速。这个主线程维护着整个窗体以及上面的子控件。当它得到一个消息,就会调用 DispatchMessage 方法派遣消息,这会引起对窗体上的

3、窗口过程的调用。窗口过程里面当然是程序员提供的窗体数据更新代码和其它代码。2、dotnet 里面的消息循环public static void Main(string args)Form f = new Form();Application.Run(f);Dotnet 窗体程序封装了上述的 while 循环,这个循环就是通过Application.Run 方法启动的。3、线程外操作 GUI 控件的问题如果从另外一个线程操作 windows 窗体上的控件,就会和主线程产生竞争,造成不可预料的结果,甚至死锁。因此 windows GUI 编程有一个规则,就是只能通过创建控件的线程来操作控件的数据,

4、否则就可能产生不可预料的结果。因此,dotnet 里面,为了方便地解决这些问题,Control 类实现了ISynchronizeInvoke 接口,提供了 Invoke 和 BeginInvoke 方法来提供让其它线程更新 GUI 界面控件的机制。public interface ISynchronizeInvokeHostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)IAsyncResult BeginInvoke(Delegate method, object args)

5、;object EndInvoke(IAsyncResult result);object Invoke(Delegate method, object args);bool InvokeRequired get; 如果从线程外操作 windows 窗体控件,那么就需要使用 Invoke 或者BeginInvoke 方法,通过一个委托把调用封送到控件所属的线程上执行。二、消息机制-线程间和进程间通信机制1、window 消息发送Windows 消息机制是 windows 平台上的线程或者进程间通信机制之一。Windows 消息值其实就是定义的一个数据结构,最重要的是消息的类型,它就是一个整数;

6、然后就是消息的参数。消息的参数可以表示很多东西。Windows 提供了一些 api 用来向一个线程的消息队列发送消息。因此,一个线程可以向另一个线程的消息队列发送消息从而告诉对方做什么,这样就完成了线程间的通信。有些 api 发送消息需要一个窗口句柄,这种函数可以把消息发送到指定窗口的主线程消息队列;而有些则可以直接通过线程句柄,把消息发送到该线程消息队列中。用消息机制通信SendMessage 是 windows api,用来把一个消息发送到一个窗口的消息队列。这个方法是个阻塞方法,也就是操作系统会确保消息的确发送到目的消息队列,并且该消息被处理完毕以后,该函数才返回。返回之前,调用者将会被

7、暂时阻塞。PostMessage 也是一个用来发送消息到窗口消息队列的 api 函数,但这个方法是非阻塞的。也就是它会马上返回,而不管消息是否真的发送到目的地,也就是调用者不会被阻塞。2、Invoke and BeginInvokeInvoke or BeginInvokeInvoke 或者 BeginInvoke 方法都需要一个委托对象作为参数。委托类似于回调函数的地址,因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了更改控件状态的代码,那么由于最终执行这个方法的是界面线程,从而避免了竞争条件,避免了不可预料的问题。如果其它线程直接操作界面线程所属的控

8、件,那么将会产生竞争条件,造成不可预料的结果。使用 Invoke 完成一个委托方法的封送,就类似于使用 SendMessage 方法来给界面线程发送消息,是一个同步方法。也就是说在 Invoke 封送的方法被执行完毕前,Invoke 方法不会返回,从而调用者线程将被阻塞。使用 BeginInvoke 方法封送一个委托方法,类似于使用 PostMessage 进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke 方法或者其它类似 WaitHandle 机制等待异步操作的完成。但是在内部实现上,Inv

9、oke 和 BeginInvoke 都是用了 PostMessage 方法,从而避免了 SendMessage 带来的问题。而 Invoke 方法的同步阻塞是靠WaitHandle 机制来完成的。3、使用场合问题如果你的后台线程在更新一个 UI 控件的状态后不需要等待,而是要继续往下处理,那么你就应该使用 BeginInvoke 来进行异步处理。如果你的后台线程需要操作 UI 控件,并且需要等到该操作执行完毕才能继续执行,那么你就应该使用 Invoke。否则,在后台线程和主截面线程共享某些状态数据的情况下,如果不同步调用,而是各自继续执行的话,可能会造成执行序列上的问题,虽然不发生死锁,但是会

10、出现不可预料的显示结果或者数据处理错误。可以看到 ISynchronizeInvoke 有一个属性,InvokeRequired。这个属性就是用来在编程的时候确定,一个对象访问 UI 控件的时候是否需要使用 Invoke或者 BeginInvoke 来进行封送。如果不需要那么就可以直接更新。在调用者对象和 UI 对象同属一个线程的时候这个属性返回 false。在后面的代码分析中我们可以看到,Control 类对这一属性的实现就是在判断调用者和控件是否属于同一个线程的。三、Delegate.BeginInvoke通过一个委托来进行同步方法的异步调用,也是.net 提供的异步调用机制之一。但是 D

11、elegate.BeginInvoke 方法是从 ThreadPool 取出一个线程来执行这个方法,以获得异步执行效果的。也就是说,如果采用这种方式提交多个异步委托,那么这些调用的顺序无法得到保证。而且由于是使用线程池里面的线程来完成任务,使用频繁,会对系统的性能造成影响。Delegate.BeginInvoke 也是讲一个委托方法封送到其它线程,从而通过异步机制执行一个方法。调用者线程则可以在完成封送以后去继续它的工作。但是这个方法封送到的最终执行线程是运行库从 ThreadPool 里面选取的一个线程。这里需要纠正一个误区,那就是 Control 类上的异步调用 BeginInvoke 并

12、没有开辟新的线程完成委托任务,而是让界面控件的所属线程完成委托任务的。看来异步操作就是开辟新线程的说法不一定准确。终于看到了,这是在判断 windows 窗体线程和当前的调用者线程是否是同一个,如果是同一个就没有必要封送了,直接访问这个 GUI 控件吧。否则,就不要那么直接表白了,就需要 Invoke 或者 BeginInvoke 做媒了。五、C#多线程 Invoke 方法的使用 在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。正确的做

13、法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。而所谓的“一面响应操作,一面添加节点” 永远只能是相对的,使 UI 线程的负担不至于太大而已,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。再举个简单例子说明下使用方法,比如你在启动一个线程,在线程的方法中想更新窗体中的一个 TextBox.using System.Threading;public d

14、elegate void MyInvoke(string str);private void btnStartThread_Click(object sender, EventArgs e) Thread thread = new Thread(new ThreadStart(DoWord);thread.Start(); public void DoWord() MyInvoke mi = new MyInvoke(SetTxt);BeginInvoke(mi,new object“abc“); public void SetTxt(string str)txtReceive.Text += str + System.Environment.NewLine;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 经营企划

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报