[转载]Winform中Control的Invoke与BeginInvoke方法
转自http://www.cppblog.com/baby-fly/archive/2010/04/01/111245.html
一、为什么 Control类提供了 Invoke和 BeginInvoke机制?
关于这个问题的最主要的原因已经是 dotnet程序员众所周知的,我在此费点笔墨再次记录到自己的日志,以便日后提醒一下自己。
1、 windows程序消息机制
Windows GUI程序是基于消息机制的,有个主线程维护着一个消息泵。这个消息泵让 windows程序生生不息。
Windows GUI 程序的消息循环
Windows程序有个消息队列,窗体上的所有消息是这个队列里面消息的最主要来源。这里的 while循环使用了 GetMessage()这个方法,这是个阻塞方法,也就是队列为空时方法就会被阻塞,从而这个 while循环停止运动,这避免了一个程序把 cpu无缘无故地耗尽,让其它程序难以得到响应。当然在某些需要 cpu最大限度运动的程序里面就可以使用另外的方法,例如某些 3d游戏或者及时战略游戏中,一般会使用 PeekMessage()这个方法,它不会被 windows阻塞,从而保证整个游戏的流畅和比较高的帧速。
这个主线程维护着整个窗体以及上面的子控件。当它得到一个消息,就会调用 DispatchMessage方法派遣消息,这会引起对窗体上的窗口过程的调用。窗口过程里面当然是程序员提供的窗体数据更新代码和其它代码。
2、 dotnet里面的消息循环
public static void Main(string[] args)
{
Form f = new Form();
Application.Run(f);
}
Dotnet窗体程序封装了上述的 while循环,这个循环就是通过 Application.Run方法启动的。
3、线程外操作 GUI控件的问题
如果从另外一个线程操作 windows窗体上的控件,就会和主线程产生竞争,造成不可预料的结果,甚至死锁。因此 windows GUI编程有一个规则,就是只能通过创建控件的线程来操作控件的数据,否则就可能产生不可预料的结果。
因此, dotnet 里面,为了方便地解决这些问题, Control 类实现了 ISynchronizeInvoke 接口,提供了 Invoke 和 BeginInvoke 方法来提供让其它线程更新 GUI 界面控件的机制。
public interface ISynchronizeInvoke
{
[HostProtection (SecurityAction .LinkDemand, Synchronization=true , ExternalThreading=true )]
IAsyncResult BeginInvoke(Delegate method, object [] args);
object EndInvoke(IAsyncResult result);
object Invoke(Delegate method, object [] args);
bool InvokeRequired { get ; }
}
}
如果从线程外操作 windows窗体控件,那么就需要使用 Invoke或者 BeginInvoke方法,通过一个委托把调用封送到控件所属的线程上执行。
二、消息机制 ---线程间和进程间通信机制
1、 window消息发送
Windows消息机制是 windows平台上的线程或者进程间通信机制之一。 Windows消息值其实就是定义的一个数据结构,最重要的是消息的类型,它就是一个整数;然后就是消息的参数。消息的参数可以表示很多东西。
Windows 提供了一些 api 用来向一个线程的消息队列发送消息。因此,一个线程可以向另一个线程的消息队列发送消息从而告诉对方做什么,这样就完成了线程间的通信。有些 api 发送消息需要一个窗口句柄,这种函数可以把消息发送到指定窗口的主线程消息队列;而有些则可以直接通过线程句柄,把消息发送到该线程消息队列中。
用消息机制通信
SendMessage是 windows api,用来把一个消息发送到一个窗口的消息队列。这个方法是个阻塞方法,也就是操作系统会确保消息的确发送到目的消息队列,并且该消息被处理完毕以后,该函数才返回。返回之前,调用者将会被暂时阻塞。
PostMessage也是一个用来发送消息到窗口消息队列的 api函数,但这个方法是非阻塞的。也就是它会马上返回,而不管消息是否真的发送到目的地,也就是调用者不会被阻塞。
2、 Invoke and BeginInvoke
Invoke or BeginInvoke
Invoke或者 BeginInvoke方 法都需要一个委托对象作为参数。委托类似于回调函数的地址,因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了 更改控件状态的代码,那么由于最终执行这个方法的是界面线程,从而避免了竞争条件,避免了不可预料的问题。如果其它线程直接操作界面线程所属的控件,那么 将会产生竞争条件,造成不可预料的结果。
使用 Invoke完成一个委托方法的封送,就类似于使用 SendMessage方法来给界面线程发送消息,是一个同步方法。也就是说在 Invoke封送的方法被执行完毕前, Invoke方法不会返回,从而调用者线程将被阻塞。
使用 BeginInvoke方法封送一个委托方法,类似于使用 PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用 EndInvoke方法或者其它类似 WaitHandle机制等待异步操作的完成。
但是在内部实现上, Invoke和 BeginInvoke都是用了 PostMessage方法,从而避免了 SendMessage带来的问题。而 Invoke方法的同步阻塞是靠 WaitHandle机制来完成的。
3、使用场合问题
如果你的后台线程在更新一个 UI控件的状态后不需要等待,而是要继续往下处理,那么你就应该使用 BeginInvoke来进行异步处理。
如果你的后台线程需要操作 UI控件,并且需要等到该操作执行完毕才能继续执行,那么你就应该使用 Invoke。否则,在后台线程和主截面线程共享某些状态数据的情况下,如果不同步调用,而是各自继续执行的话,可能会造成执行序列上的问题,虽然不发生死锁,但是会出现不可预料的显示结果或者数据处理错误。
可以看到 ISynchronizeInvoke有一个属性, InvokeRequired。这个属性就是用来在编程的时候确定,一个对象访问 UI控件的时候是否需要使用 Invoke或者 BeginInvoke来进行封送。如果不需要那么就可以直接更新。在调用者对象和 UI对象同属一个线程的时候这个属性返回 false。在后面的代码分析中我们可以看到, Control类对这一属性的实现就是在判断调用者和控件是否属于同一个线程的。
三、 Delegate.BeginInvoke
通过一个委托来进行同步方法的异步调用,也是 .net提供的异步调用机制之一。但是 Delegate.BeginInvoke方法是从 ThreadPool取出一个线程来执行这个方法,以获得异步执行效果的。也就是说,如果采用这种方式提交多个异步委托,那么这些调用的顺序无法得到保证。而且由于是使用线程池里面的线程来完成任务,使用频繁,会对系统的性能造成影响。
Delegate.BeginInvoke也是讲一个委托方法封送到其它线程,从而通过异步机制执行一个方法。调用者线程则可以在完成封送以后去继续它的工作。但是这个方法封送到的最终执行线程是运行库从 ThreadPool里面选取的一个线程。
这里需要纠正一个误区,那就是Control类上的异步调用BeginInvoke并没有开辟新的线程完成委托任务,而是让界面控件的所属线程完成委托任务的。看来异步操作就是开辟新线程的说法不一定准确。
作者讲解的非常清晰,有MFC编程经验的人应该会有这么一种感慨“原来Invoke”就是SendMessage,“BeginInvoke”就是Postmessage。
另外,需要注意的是,不论是Invoke还是BeginInvoke,委托引用的方法均需要在UI线程中完成执行,所以不要把耗时的操作放到Invoke或BeginInvoke的执行函数中,否则会出现界面卡顿现象。
[转载]Winform中Control的Invoke与BeginInvoke方法的更多相关文章
- 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)
[分析]浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang) 今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间 ...
- C#中Control的Invoke和BeginInvoke是相对于支线线程
近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下. Control的Invoke和BeginInvoke 是相对于支线线程(因为一般在支线线程中调用, ...
- 控件的invoke和beginInvoke方法
System.Windows.Forms.Timer 的timer是在主线程上执行的,因此在timer的tick事件中操作界面上的控件不会发生线程的安全性检测. Control的invoke和begi ...
- Control的Invoke和BeginInvoke
转载:https://www.cnblogs.com/c2303191/articles/826571.html 近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资 ...
- Control的Invoke和BeginInvoke详解
(一)Control的Invoke和BeginInvoke 我们要基于以下认识: (1)Control的Invoke和BeginInvoke与Delegate的Invoke和BeginInvoke是不 ...
- [转]Control的Invoke和BeginInvoke
转自:Control的Invoke和BeginInvoke 作者:Kuffy Wang 近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下.感谢这篇文 ...
- C#委托同步异步说明,并比较control调用Invoke和BeginInvoke的异同
一.委托的同步和异步: 1.同步 使用Invoke调用同步,或直接写fun1("func"),在fun1.Invoke这一步会明显的阻塞线程 使用: static void Mai ...
- delegate的Invoke和BeginInvoke方法
C#中的控件和delegate委托方法都有Invoke和BeginInvoke方法,控件的这两个方法网上讲得很多, 这里就不多说了,下面讲一下delegate的Invoke和BeginInvoke方法 ...
- [转载] Winform中几种好用的Grid Control
转自 博客园 http://www.cnblogs.com/peterzb/archive/2009/05/29/1491781.html 1.DataGridView .net 2.0框架自带的网 ...
随机推荐
- DFS - leetcode [深度优先遍历]
最短路径=>BFS 所有路径=>DFS 126. Word Ladder II BFS+DFS: BFS找出下一个有效的word进队 并记录step 更新两个变量:unordered ...
- Kruskal算法的简单实现
嘛嘛嘛,好像大家在实现Kruskal算法是都是用的边集数组,判断图的连通性咱不会,o(╯□╰)o(为什么咱这么菜诶) Kruskal算法: 规则: (1)对每一条边按照从小到大进行排序 (2)加入边的 ...
- B/S和C/S【转载Jane的博客 http://blog.sina.com.cn/liaojane】
什么是C/S和B/S结构? C/S又称Client/Server或客户/服务器模式.服务器通常采用高性能的PC.工作站或小型机,并采用大型数据库系统,如Oracle.Sybase.In ...
- DIV+CSS 让同一行的图片和文字对齐
在div+css布局中,如果一行(或一个DIV)内容中有图片和文字的话,图片和文字往往会一个在上一个在下,这是一个新手都会遇到问题,我的解决方法有三: 1.添加CSS属性:vertical-align ...
- Java Day03 面向对象程序设计
1.面向对象 面向对象是指一种程序设计泛型,同时也是一种程序开发的方法. 2.类 类是一种抽象的概念,类中包含了数据与对数据的操纵. 具有相同特性(数据元素)和行为(功能)的对象的抽象就是类.类是对象 ...
- Hiver 操作 MySQL 导致锁表
Hadoop 搬迁到新集群后,操作主库 MySQL 导致了锁表...sad 具体锁表时间点 : 2016-1-14 14:31 到 2016-1-14 14:36 之间 在 oradba 的 ...
- python安装pip
pip类似RedHat里面的yum,安装Python包非常方便.本节详细介绍pip的安装.以及使用方法. 1.pip下载安装 1.1 pip下载 # wget "https://pypi.p ...
- 总结一下js的原型和原型链
最近学习了js的面向对象编程,原型和原型链这块是个难点,理解的不是很透彻,这里搜集了一些这方面的资料,以备复习所用 一. 原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一 ...
- ARM应用调试思路、方法总结、笔记
一.应用调试1:使用strace命令来跟踪系统调用 二.应用调试2:使用GDB来调试应用程序 编译gdb,gdbservertar xjf gdb-7.4.tar.bz2cd gdb-7.4/./co ...
- # 欢迎使用Markdown编辑器写博客
似的发射点 甜甜 他inn他 absct{ for i 士大夫似的 胜多负少 import os import sys import subprocess import textwrap if sys ...