【转】Invoke 和 BeginInvoke 的区别

简介: 讨论环境:C# 、.net、VS2005 .net默认所有的可视窗体在主线程内维护,如果某工作线程(主线程之外)想实现对窗体控件的操作,缺省情况下是不允许直接操作的,而要通过 Invoke 方法将其封送到主线程去完成。

讨论环境:C# .netVS2005

.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 错误,帮助对这个异常的说明是“挂起的方法调用过多而导致执行堆栈溢出时引发的异常”。看来还是要慎用。

http://www.cnblogs.com/ylgqq/archive/2007/12/28/1018760.html

相关文章
|
算法 图形学
三维球体空间中光线反射模拟与三维点云提取matlab仿真
本项目使用MATLAB2022A模拟三维椭球体内光线反射并提取三维点云。通过设置椭球模型作为墙壁,根据几何光学原理计算光线在曲面上的反射路径,记录每次反射点坐标,生成三维点云图。核心代码实现多次反射的循环计算与绘图,并展示反射点的位置变化及其平滑处理结果。最终,通过光线追踪技术模拟真实场景中的光线行为,生成精确的三维点云数据,适用于计算机图形学和光学仿真领域。
524 27
|
人工智能 自然语言处理 PyTorch
基于openi平台免费华为昇腾910B芯片部署qwen2.5 Instruct 14B大模型
基于OpenI平台和华为昇腾910B芯片,本方案详细介绍了如何免费部署Qwen-2.5 Instruct 14B大模型。涵盖准备工作、模型适配、部署步骤及性能优化等内容,适用于NLP任务部署、本地化适配及实时服务化等多种应用场景。
3858 1
|
C# Windows
c#学习系列相关之多线程(三)----invoke和begininvoke
c#学习系列相关之多线程(三)----invoke和begininvoke
1903 0
|
安全 Ubuntu Shell
深入挖掘Debian系统中安装Docker
【8月更文挑战第21天】在Debian系统中安装Docker需按步骤操作:首先确保软件包更新,执行`sudo apt update`并安装必要软件包支持HTTPS;接着添加Docker官方GPG密钥以验证包的完整性和安全性;然后设置Docker稳定版仓库,通过`tee`命令配置仓库文件;再更新软件包索引;最后安装Docker Engine并通过运行测试容器确认安装成功。此指南适用于多数Debian版本,如遇问题请查阅官方文档。
938 0
|
机器学习/深度学习 人工智能 算法
目标检测技术研究现状及发展趋势
随着人工智能、深度学习技术的快速发展,受到深度学习在自然场景图像目标检测中的成功应用的影响,许多学者尝试将深度学习方法应用于图像的目标检测中,基于卷积神经网络的目标检测成为发展趋势。
2033 0
目标检测技术研究现状及发展趋势
|
存储 安全 搜索推荐
什么是PDS
简述什么是PDS以及基于PDS搭建的企业网盘的体验感受
|
供应链 安全 算法
FSC公链金融项目开发特性分析
FSC公链是基于FSC技术建构的公共区块链网络
|
弹性计算 负载均衡 小程序
阿里云服务器免费申请3个月试用攻略
阿里云服务器免费申请3个月试用攻略,阿里云服务器免费试用申请链接入口,阿里云个人用户和企业用户均可申请免费试用,最高可以免费使用3个月,阿里云服务器网分享阿里云服务器免费试用申请入口链接及云服务器配置
4547 0
|
智能硬件
Apple Watch续航有问题?你需要一根这样的数据线
Apple Watch今天公布了一大堆应用,让人对这款前所未有的智能手表更加期待、兴奋了,估计不少果粉和“大表哥”已经心痒难耐了。不过,它18个小时的续航,还是让不少人心有戚戚。
298 0
Apple Watch续航有问题?你需要一根这样的数据线
|
JavaScript 前端开发 程序员
那些让人直呼卧槽的Python代码!
Python 是一个设计优美的解释型高级语言, 它提供了很多能让程序员感到舒适的功能特性。但有的时候, Python 的一些输出结果对于初学者来说似乎并不是那么一目了然。
394 0