Dispatcher.Invoke 同步执行会阻塞调用线程,适合需立即获取结果的场景;BeginInvoke 异步但已过时;InvokeAsync 是推荐方案,返回 Task 支持 await,需注意线程上下文与异常处理。

当你在非 UI 线程(比如后台任务)中修改 WPF 控件,必须通过 Dispatcher 调度到 UI 线程。用 Invoke 时,当前线程会停住,等 UI 线程执行完委托才继续往下走。
常见错误现象:在循环里反复调用 Invoke 更新进度条,界面卡顿、响应变慢,甚至看起来“假死”——其实不是崩溃,是调用线程被一个个堵住了。
ActualWidth 或触发一次必须完成的布局更新Invoke 支持重载指定超时(TimeSpan),超时会抛 TimeoutException
BeginInvoke 把委托“扔进”UI 线程消息队列后立刻返回,调用线程不会停。它更像发个通知:“请稍后处理这个”,自己该干啥干啥。
常见错误现象:调用 BeginInvoke 后立即访问刚创建的控件字段,发现还是 null;或者更新了 TextBlock.Text,但下一行就去断言其值,结果断言失败——因为赋值还没发生。
IsEnabled、触发动画,不需要立刻读取结果DispatcherOperation,可调用 .Wait() 强制同步(但这就退化成 Invoke 的行为)BeginInvoke 已标记为 [Obsolete],推荐改用 InvokeAsync(返回 Task,语义更清晰)InvokeAsync 返回 Task,支持 await,既避免了 Invoke 的阻塞,又比 BeginInvoke 更容易控制执行时机和错误处理。
使用场景错位时的问题:用 await Dispatcher.InvokeAsync(...) 看似“异步”,但如果在 UI 线程上调用它,会同步执行(无调度开销),而你在后台线程 await 它,就会引发跨线程上下文切换开销——不是 bug,但可能比预期慢。
BeginInvoke
Task 中,需用 try/catch 包裹 await 表达式,不能只 try 外层Invoke 一样支持优先级枚举(DispatcherPriority),比如设为 Background 可降低对用户交互的干扰await Dispatcher.InvokeAsync(() =>
{
statusText.Text = "Processing...";
}, DispatcherPriority.Normal);
最隐蔽的问题不是选错方法,而是误判“当前是否在 UI 线程”。直接调用 Dispatcher.CheckAccess() 比硬编码判断更可靠。
比如在 Loaded 事件里启动一个 Task.Run,然后在里面调用 Dispatcher.Invoke ——看着合理,但如果窗体还没完全初始化好,Dispatcher 可能为 null,或引发 InvalidOperationException: “The calling thread cannot access this object because a different thread owns it.”
if (Dispatcher?.CheckAccess() == true),再决定是否调度Dispatcher 引用,应在 Loaded 之后或首次访问时惰性获取Dispatcher 和 WinForms 的 Control.Invoke 行为相似,但对象模型不同,混用会导致编译失败或运行时异常