Invoke 和 BeginInvoke 的区别

Invoke 和 BeginInvoke 的区别net 默认所有的可视窗体在主线程内维护 如果某工作线程 主线程之外 想实现对窗体控件的操作 缺省情况下是不允许直接操作的 而要通过 Invoke 方法将其封送到主线程去完成 在 Control 类内提供了 Invoke 和 BeginInvoke 两个方法实现该功能 MSDN 帮助中提到 它们的唯一区别是 BeginInvoke 多了 异步执行 四个字 两方法的具体帮助请自行查看 MS

 

.net默认所有的可视窗体在主线程内维护,如果某工作线程(主线程之外)想实现对窗体控件的操作,缺省情况下是不允许直接操作的,而要通过 Invoke 方法将其封送到主线程去完成。在Control 类内提供了 Invoke BeginInvoke 两个方法实现该功能,MSDN 帮助中提到,它们的唯一区别是 BeginInvoke 多了“异步执行”四个字。(两方法的具体帮助请自行查看MSDN,这里不多罗嗦了)。

“异步执行”怎么理解,查了网上的一些解答,通过Reflector查看了两方法的背后源码后,得出如下结论:

Invoke 引起工作线程的阻塞,BeginInvoke 不引起工作线程的阻塞。

具体解释一下:我们先假设称主线程(即窗体控件的拥有者)为A线程,工作线程为B线程,如果B线程需要操作窗体控件,那么就要使用 Invoke(或BeginInvoke),将相应的操作通过代理,封送到主线程A(具体的代码实现,不多罗嗦,假设读者已知)。那么.net背后是怎么实现线程间“任务挪移”这一步操作的呢?通过Reflector查看源码后发现,原来 Invoke 将你交给它的委托封装成了一个标准的Windows消息,加进了主线程的消息队列内。回到 Invoke BeginInvoke 的区别上来,如果使用 Invoke,那么 B 线程必须等到A线程响应了传送的消息后 才能得到返回值,而如果使用BeginInvoke,则B线程将消息送到A线程后,马上返回,并不一定等待该消息被A线程响应完成。所以如果A线程处在繁忙状态或休眠状态,使用 Invoke 封送消息就会使得B线程被堵塞,而是用 BeginInvoke 则不然,这就是所谓的“异步执行”了。

空口无凭,让我们来看看Invoke BeginInvoke 背后的代码:

public object Invoke(Delegate method, params object[] args)
{ 
  
    using (MultithreadSafeCallScope scope = new MultithreadSafeCallScope())
    { 
  
        return this.FindMarshalingControl().MarshaledInvoke(this, method, args, true);
    }
}
 
public IAsyncResult BeginInvoke(Delegate method, params object[] args)
{ 
  
    using (MultithreadSafeCallScope scope = new MultithreadSafeCallScope())
    { 
  
        return (IAsyncResult) this.FindMarshalingControl().MarshaledInvoke(this, method, args, false);
    }
}

两方法对应的代码基本一样,除了返回类型外,还有一处细微的差别 MarshaledInvoke 方法的第三个参数:Invoke trueBeginInvoke false。这个参数表示什么意思呢?把MarshaledInvoke 背后的代码拉出来看看:

private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
{ 
  
    int num;
    if (!this.IsHandleCreated)
    { 
  
        throw new InvalidOperationException(SR.GetString("ErrorNoMarshalingThread"));
    }
    if (((ActiveXImpl) this.Properties.GetObject(PropActiveXImpl)) != null)
    { 
  
        IntSecurity.UnmanagedCode.Demand();
    }
    bool flag = false;
    if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out num) == SafeNativeMethods.GetCurrentThreadId()) && synchronous)
    { 
  
        flag = true;
    }
    ExecutionContext executionContext = null;
    if (!flag)
    { 
  
        executionContext = ExecutionContext.Capture();
    }
    ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);
    lock (this)
    { 
  
        if (this.threadCallbackList == null)
        { 
  
            this.threadCallbackList = new Queue();
        }
    }
    lock (this.threadCallbackList)
    { 
  
        if (threadCallbackMessage == 0)
        { 
  
            threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_ThreadCallbackMessage");
        }
        this.threadCallbackList.Enqueue(entry);
    }
    if (flag)
    { 
  
        this.InvokeMarshaledCallbacks();
    }
    else
    { 
  
        UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
    }
     
if (!synchronous)
    { 
    
        return entry;
    }
    if (!entry.IsCompleted)
    { 
    
        this.WaitForWaitHandle(entry.AsyncWaitHandle);
    }

    if (entry.exception != null)

    { 
  
        throw entry.exception;
    }
    return entry.retVal;

}

好长的一段代码,看着都眼晕。看看刚才提到的第三个参数的名字 synchronous ,从字面意思看‘是否同步’,OK,再看看最后被加粗的几行代码(还是根据字面意思猜):如果不同步,立刻返回;如果同步,而且没完事,就等一等,最后给出返回值。如果我们的假设成立,那么 Invoke 方法的第三个参数为True,就是要同步;BeginInvoke 方法的第三个参数为 False,就是不同步,即异步。Right,到这里就和 MSDN 帮助上介绍 BeginInvoke “异步执行”就对上了。

如果按照数学分析归纳法,到此应该说:由以上分析,得证:

Invoke 引起工作线程的阻塞,BeginInvoke 不引起工作线程的阻塞。

最后,要补充两点:第一,如果主线程以外的工作线程要操作窗体控件,并非一定要使用Invoke方法,更改下边这个属性也是 OK 的,只是不推荐使用。

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;

第二,既然 BeginInvoke 不引起阻塞,那么是否就说明它比 Invoke 好呢。我在自己的程序里,把所有的 Invoke 均变成 BeginInvoke ,运行一段时间后,结果提示 StackOverFlowException 错误,帮助对这个异常的说明是“挂起的方法调用过多而导致执行堆栈溢出时引发的异常”。看来还是要慎用。

===========================2011-03-10=======================================

最近由于工作的需要,又纠缠于这个问题,我自己之前未把这个问题讲的很明白,特转载引用他人之成果

 http://www.cnblogs.com/worldreason/archive/2008/06/09/1216127.html

http://www.cnblogs.com/whssunboy/archive/2007/06/07/775319.html

生活TMD需要激情,做事需冷静,说话需冷静!

遇事记着:办法总比困难多,困难和问题说不定就是机遇和转折!

历史证明:哪个环节没照顾到,哪个环节就会出问题!能自己来,就不要让别人来。

 

本文转载自:http://www.cnblogs.com/ylg/archive/2007/12/28/1018760.html

特此感谢博主:

幸福清扬

之技术学习

 

 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205620.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午5:35
下一篇 2026年3月19日 下午5:36


相关推荐

  • android Apk打包过程概述_android是如何打包apk的「建议收藏」

    最近看了老罗关于android应用资源编译和打包过程的分析,过程比较复杂,这里结合其他资料,简要描述下打包过程,从整体上了解这个流程,感兴趣的再去详细研究过程。流程概述:1、打包资源文件,生成R.java文件2、处理aidl文件,生成相应java 文件3、编译工程源代码,生成相应class 文件4、转换所有class文件,生成classes.dex文件5、打包生成apk6、对apk文件进行签名7、

    2022年3月11日
    58
  • Compilation failed to complete

    Compilation failed to complete

    2021年9月30日
    48
  • quick-cocos2d-x android返回键监听并实现原生退出对话框

    quick-cocos2d-x android返回键监听并实现原生退出对话框

    2021年12月2日
    50
  • 计算机网络基础知识整理「建议收藏」

    参考书目:计算机网络(第6版谢希仁)一、概述1、三网:电信网络、有线电视网络和计算机网络2、电路交换、报文交换、分组交换的比较电路交换公共电话网(PSTN网)和移动网(包括GSM和CDMA网)采用的都是电路交换技术,它的基本特点是采用面向连接的方式,在双方进行通信之前,需要为通信双方分配一条具有固定宽带的通信电路,通信双方在通信过程中一直占用所分配的资源,直到通信结束,并且在电…

    2022年4月17日
    82
  • Java8中String.join方法,让我们的代码更优美

    Java8中String.join方法,让我们的代码更优美强烈推荐一个大神的人工智能的教程:http://www.captainbed.net/zhanghan【前言】距Java8(14年3月19日)发布马上就四年了;相信接触过java8的人,会对它的很多新的特性新的方法有感觉;自然我也不例外;今天就一起给大家分享一个java8中的方法。【善用新语法,让代码更优美】一、简洁:最近自己在做项目的时候学习到很多技巧…

    2022年6月4日
    49
  • 有关于pycharm镜像源的问题

    有关于pycharm镜像源的问题有关于 pycharm 镜像源的问题常用镜像源注意问题常用镜像源国内常用镜像源 清华 https pypi tuna tsinghua edu cn simple 阿里云 http mirrors aliyun com pypi simple 中国科技大学 https pypi mirrors ustc edu cn simple 百度 https mirror baidu com pypi simple 注意问题有关于在网上常见的在 pycharm 配置里修改镜像源 经本人测试 修改

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号