询问者
Control.Invoke的原理

常规讨论
-
如果说目前.NET里有什么最让我心痒的话,那就是这个函数了。我极度地想知道这个函数到底干了些什么。
按MSDN的说法,此函数:在拥有此控件的基础窗口句柄的线程上执行指定的委托。
很俨然的是,我们没法让一个线程停止当前的工作(不管它正在运行还是挂起),转去执行另外一些工作。因此,Invoke函数肯定不会去操作什么线程之类的。但是,Invoke的调用又确实是在”拥有此控件的基础窗口句柄的线程"上执行的。它是怎么做到的?
PowerBuilder里有一个关键字,Post,根据说明,这个关键字与消息队列有关,我还曾经用它很方便实现过一些数据验证之类的功能。
在我看来,Invoke的原理唯一的可能也是利用消息机制。通过把调用封装成消息,发送到窗体的消息队列,这样就可以由窗体所属线程来执行调用了。
然而,最近的一项测试似乎推翻了这个猜想。
private void CloseDialog() { Thread.Sleep(5000); Dialog.Invoke(new InovkeDelegate(Dialog.Close)); } private void button1_Click(object sender, EventArgs e) { Thread thread = new Thread(new ThreadStart(this.CloseDialog)); thread.Start(); Dialog.ShowDialog(); }
若Invoke如我想象中那般工作,则此段代码运行时,弹出框应当永远不会被关闭。因为在ShowDialog在窗体关闭前不会返回,窗口过程也不会结束。而Invoke发送的消息在当前消息未处理完(即窗口过程未结束)之前不会被执行。
但现实是,弹出框自动关闭了。
哪位大牛有更好的解释呢?
有关我对Invoke与消息队列的推测:
http://hi.baidu.com/yedaoq/blog/item/c56771ed9ed59bddb21cb1e7.html- 已编辑 泉子 2009年7月10日 10:02
全部回复
-
您好,:
对Control.Invoke的研究可以下载微软公布的源代码;或直接在调试器上进行跟踪。以下是个人理解,仅供参考:
Control的定义,
public class Control : Component, IDropTarget,
ISynchronizeInvoke, IWin32Window, IBindableComponent, IComponent, IDisposable(省略了特性)
其中有个接口实现同步调用的,ISynchronizeInvoke。
Control对该接口中的Invoke方法进行了实现。大致原理是在新线程通过委托来进行异步调用,而且调用绑回到产生新线程的窗体线程上。
MSDN上“在拥有此控件的基础窗口句柄的线程上执行委托。”个人理解就是指与该控件的窗口同一线程,因为windowsForm的消息机制,使得不能在不同的线程上接收消息。那么Invoke实际上是由窗口的线程来调用的。估计这也是接口所表示的同步调用的含义。
个人推算您代码的运行方式,到button1_Click中的thread.Start();窗口的线程并不堵塞,继续往下执行Dialog.ShowDialog();会打开一个新窗口。
同时在一个新的线程上执行CloseDialog(),先休眠5秒。当运行到Dialog.Invoke(new InovkeDelegate(Dialog.Close));时.net将线程重新绑回窗口所在线程,Invoke将阻塞当前线程,直到委托执行完毕,因此弹出的对话窗体会关闭。
我将您的代码重新设计了一下,以方便证明我的观点,如下:
public partial class InvokeForm : Form
{
//定义被调用的对话窗口,为避免误导笔者将Dialog重命名,表示这是一个窗口对话,不是其它类型,
//例如FolderBrowserDialog,FolderBrowserDialog并不继承Control。
Form1 _DialogForm;
//定义一个委托
public delegate void InovkeDelegate();
public InvokeForm()
{
InitializeComponent();
_DialogForm = new Form1();
}
private void CloseDialog()
{
Thread.Sleep(5000);
MessageBox.Show("New Thread Start:" + Thread.CurrentThread.ManagedThreadId.ToString());//显示新线程ID 在笔者本机上是11
_DialogForm.Invoke(new InovkeDelegate(ShowThreadID));));//调用Invoke方法将被邦回调用窗口线程,正体现了窗口的消息机制,
//也解释了“在拥有此控件的基础窗口句柄的线程上执行委托”
_DialogForm.Invoke(new InovkeDelegate(_DialogForm.Close));
MessageBox.Show("New Thread End:" + Thread.CurrentThread.ManagedThreadId.ToString());//又回到新线程,显示新线程ID 在笔者本机上是11}
public void ShowThreadID()
{
//显示返回调用窗口的线程值,笔者本机上是10,与调用方一致
MessageBox.Show("Return InvokeForm Thread:" + Thread.CurrentThread.ManagedThreadId.ToString());
}private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("InvokeForm:" + Thread.CurrentThread.ManagedThreadId.ToString());//显示调用窗口的线程ID 在笔者本机上是10
Thread thread = new Thread(new ThreadStart(this.CloseDialog));
thread.Start();
_DialogForm.ShowDialog();}
}- 已编辑 Jiyuan 2009年7月12日 10:03