none
Control.Invoke的原理 RRS feed

  • 常规讨论

  • 如果说目前.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
    2009年7月10日 9:59

全部回复

  • 您好,:
    对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
    2009年7月12日 9:52
  • 画调用流程图,InvokeForm线程--->新线程--->InvokeForm线程--->新线程--->结束
    2009年7月12日 10:21
  • 在CSDN上了同一帖子,基本上有结果了。
    http://topic.csdn.net/u/20090710/23/ca6502ae-06f4-4337-991b-5f13ebf22bd9.html?20786
    虽然不大明白您所说的“绑回”是什么意思,但大概意思应该是一样的。谢谢!
    2009年7月13日 5:10