惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
T
Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
Intezer
C
Cyber Attacks, Cyber Crime and Cyber Security
The Register - Security
The Register - Security
量子位
Security Latest
Security Latest
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
MyScale Blog
MyScale Blog
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
Spread Privacy
Spread Privacy
Jina AI
Jina AI
博客园 - 【当耐特】
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
SecWiki News
SecWiki News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
G
GRAHAM CLULEY
宝玉的分享
宝玉的分享
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
The Blog of Author Tim Ferriss
V
Vulnerabilities – Threatpost
有赞技术团队
有赞技术团队
T
Tor Project blog
H
Hacker News: Front Page
A
Arctic Wolf
NISL@THU
NISL@THU
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
V
V2EX
N
News and Events Feed by Topic
Webroot Blog
Webroot Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
I
InfoQ
D
Docker
L
LINUX DO - 最新话题
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
U
Unit 42

博客园 - 杨连国

win10 IoT开发 Serial​Device 返回 null .NET Delegates: A C# Bedtime Story【转载】 C++ 和 C# 变量定义的一点区别 变量定义 接口(CLR via C# 读书笔记) 生成器模式(builder) 类型和成员基础(CLR via C#) 基本类型、引用类型、值类型——(CLR via C#) 抽象工厂模式(abstract factory) 李建忠C#面向对象设计模式纵横谈 —— 单件模式 李建忠C#面向对象设计模式纵横谈 —— 面向对象设计模式与原则 程序设计和重构的目标 OO设计原则——读《你必须知道的.NET》 输入法灰掉bug解决 半角全角Bug解决 由迭代模式想到得——用设计模式理解 .NET ASCII 表设计之妙 调用VC封装的DLL时,两个类型匹配问题 调用非托管Dll
Invoke 和 BeginInvoke 的区别
杨连国 · 2007-12-28 · via 博客园 - 杨连国

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

===========================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