【转】Invoke 和 BeginInvoke 的区别

C#中Invoke与BeginInvoke详解
探讨了C#中Invoke与BeginInvoke方法在多线程环境下对UI操作的不同作用,详细解析了两者背后的实现机制及异步执行的概念。

讨论环境: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

转载于:https://www.cnblogs.com/cappuccino/archive/2010/12/20/1911226.html

C# 中,`Invoke` `BeginInvoke` 都是用于在委托(`delegate`)上调用方法的机制,它们的区别主要在于: | 特性 | `Invoke` | `BeginInvoke` | |------|----------|---------------| | **调用方式** | 同步调用 | 异步调用 | | **是否阻塞当前线程** | 是 | 否 | | **执行线程** | 同一线程(调用线程) | 可能是线程池线程 | | **返回值** | 返回方法的返回值 | 返回 `IAsyncResult`,需要配合 `EndInvoke` 使用 | | **是否需要等待** | 是 | 否 | | **常用于** | 确保方法执行完成 | 后台执行、异步操作 | --- ## 🧠 举个简单例子说明区别: ```csharp delegate void MyDelegate(); public void TestMethod() { Console.WriteLine("TestMethod 执行在线程:" + Thread.CurrentThread.ManagedThreadId); } public void Run() { MyDelegate del = TestMethod; Console.WriteLine("主线程 ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("开始 Invoke"); del.Invoke(); Console.WriteLine("结束 Invoke"); Console.WriteLine("开始 BeginInvoke"); del.BeginInvoke(null, null); Console.WriteLine("结束 BeginInvoke"); } ``` ### 输出结果(示例): ``` 主线程 ID:1 开始 Invoke TestMethod 执行在线程:1 结束 Invoke 开始 BeginInvoke 结束 BeginInvoke TestMethod 执行在线程:3 ``` 可以看到: - `Invoke` 是**同步执行**,会阻塞当前线程直到方法执行完。 - `BeginInvoke` 是**异步执行**,立即返回,方法在另一个线程上执行。 --- ## 🔁 更详细的对比: | 项目 | `Invoke` | `BeginInvoke` | |------|----------|---------------| | 调用方式 | 同步 | 异步 | | 是否阻塞 | 是 | 否 | | 是否需要回调 | 否 | 是(可选) | | 是否需要 `EndInvoke` | 否 | 是(否则可能造成资源泄漏) | | 异常处理 | 直接抛出 | 必须在 `EndInvoke` 中捕获 | --- ## 🧩 什么时候用 `Invoke`? - 你需要等待方法执行完成。 - 你需要获取方法的返回值。 - 你需要在 UI 线程更新控件(在 WinForms/WPF 中)。 ```csharp this.Invoke((MethodInvoker)delegate { label1.Text = "更新 UI"; }); ``` --- ## 🧩 什么时候用 `BeginInvoke`? - 你想执行一个耗时操作,不想阻塞当前线程。 - 你想异步执行一个方法,并在完成后通过回调处理结果。 - 你希望提高程序的响应性(如网络请求、文件读取等)。 ```csharp del.BeginInvoke(ar => { Console.WriteLine("异步执行完成"); }, null); ``` > ⚠️ 注意:使用 `BeginInvoke` 后,必须调用 `EndInvoke` 来结束调用,否则可能会有资源泄漏。 --- ## 🧠 `BeginInvoke` 的完整用法: ```csharp IAsyncResult result = del.BeginInvoke(ar => { Console.WriteLine("异步操作完成"); del.EndInvoke(ar); // 必须调用 }, null); ``` --- ## ✅ 总结对比表: | 比较点 | `Invoke` | `BeginInvoke` | |--------|----------|---------------| | 是否同步 | ✅ 是 | ❌ 否 | | 是否阻塞 | ✅ 是 | ❌ 否 | | 是否需要等待完成 | ✅ 是 | ❌ 否 | | 是否需要 `EndInvoke` | ❌ 否 | ✅ 是 | | 是否适合 UI 更新 | ✅ 是 | ❌ 否(除非确保在 UI 线程) | | 是否异步 | ❌ 否 | ✅ 是 | --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值