通八洲科技

c# 委托的BeginInvoke和EndInvoke是什么 异步委托和Task的区别

日期:2025-12-30 00:00 / 作者:星降
BeginInvoke/EndInvoke 是已过时的委托异步调用机制,因资源泄漏、无取消支持、不可 await 等缺陷被 Task.Run + async/await 取代;仅 WinForms 的 Control.BeginInvoke 因消息机制例外存在。

BeginInvoke/EndInvoke 是 .NET 早期 APM(异步编程模型)下委托的异步调用机制,本质是线程池 + 回调;而 Task 是 TPL(任务并行库)提供的现代异步抽象,更灵活、可组合、支持 await —— 它们不是同一层概念,也不推荐混用。

为什么现在几乎不用 BeginInvoke/EndInvoke

它们属于已过时(obsolete-in-practice)的异步模式,仅在极少数遗留 WinForms 控件跨线程调用(如 Control.BeginInvoke)中还有意义,但那和委托的 BeginInvoke 不是一回事。委托自身的 BeginInvoke 在 .NET Core / .NET 5+ 中已被标记为“不推荐使用”,且无对应 await 支持。

Task 怎么替代委托的异步调用?

直接把耗时逻辑封装进 FuncFunc,再用 Task.Run 托管到线程池 —— 简单、可控、可 await、可取消。

public delegate int HeavyCalcDelegate(int x, int y);

// ❌ 过时写法(不推荐)
HeavyCalcDelegate calc = (a, b) => { Thread.Sleep(1000); return a + b; };
IAsyncResult ar = calc.BeginInvoke(5, 3, null, null);
int result = calc.EndInvoke(ar); // 阻塞等待

// ✅ 现代写法(推荐)
Task task = Task.Run(() => { Thread.Sleep(1000); return 5 + 3; });
int result2 = await task; // 或 task.Result(阻塞)

什么时候真得用 BeginInvoke

基本没有。唯一常见例外是 WinForms 中跨线程更新 UI:

// 注意:这是 Control 的 BeginInvoke,不是 Delegate 的!
this.BeginInvoke(new Action(() =>
{
    label1.Text = "Done";
}));

真正该警惕的是:看到 BeginInvoke 就条件反射想“是不是要异步”,先确认它是哪个类型的成员 —— 委托?控件?还是自定义类?多数情况下,你想要的只是 Task.Runasync/await