Invoke()和BeginInvoke()有什么区别?

449

想知道 BeginInvoke()Invoke() 之间有什么区别?

主要是它们各自的用途。

编辑:创建线程对象并在其上调用invoke与只在委托上调用 BeginInvoke() 之间有什么区别?还是它们是一样的?


注意: 在编译时,refout参数都被视为相同,但在运行时是不同的。 - DhanRaj Wanjare
6个回答

631
你是指 Delegate.Invoke/BeginInvoke 还是 Control.Invoke/BeginInvoke
  • Delegate.Invoke: 在同一线程上同步执行。
  • Delegate.BeginInvoke: 在一个线程池线程上异步执行。
  • Control.Invoke: 在UI线程上执行,但调用线程会在继续之前等待完成。
  • Control.BeginInvoke: 在UI线程上执行,且调用线程不等待完成。

Tim's 的回答提到了你何时可能想使用 BeginInvoke,尽管它大多数是针对 Delegate.BeginInvoke 的,我猜测。

对于 Windows Forms 应用程序,我建议通常应该使用 BeginInvoke。这样你就不需要担心死锁等问题 - 但你需要了解 UI 可能在你下次查看它时还没有更新!特别是,你不应修改 UI 线程可能即将用于显示目的的数据。例如,如果你有一个带有 FirstNameLastName 属性的 Person,并做了以下操作:

person.FirstName = "Kevin"; // person is a shared reference
person.LastName = "Spacey";
control.BeginInvoke(UpdateName);
person.FirstName = "Keyser";
person.LastName = "Soze";

如果出现这种问题,UI可能会显示“Keyser Spacey”。(在内存模型的奇异作用下,它也有可能显示“Kevin Soze”)。

然而,除非你遇到了这种问题,Control.BeginInvoke更容易正确使用,并且可以避免后台线程无谓等待。请注意,Windows Forms团队保证您可以以“fire and forget”的方式使用Control.BeginInvoke,即不必调用EndInvoke。这并非对于所有异步调用都适用:通常每个BeginXXX都应该有一个相应的EndXXX调用,通常在回调中执行。


4
为什么人们会选择使用 Invoke 而不是 BeginInvoke?使用 Invoke 有什么优势吗?两者都可以在后台执行进程,只是一个在同一线程上,另一个在不同线程上? - yeeen
2
@Jon:当我使用Dispatcher.BeginInvoke时,我的代码运行良好,但是在Dispatcher.Invoke中,我的应用程序让我等待几秒钟,然后初始化所有控件,然后启动。你能帮我找出我卡在哪个地方了吗? - SharpUrBrain
6
Control.BeginInvoke与Dispatcher.BeginInvoke类似,但适用于WinForms(而Dispatcher适用于WPF和Silverlight)。 - Jon Skeet
4
我建议您提出具体问题,而不是在评论中继续讨论,当然要先检查是否已有其他人提出了相同的问题。 - Jon Skeet
2
@AZ:是的,他所谓的“在UI线程上”是指该特定“UI线程”拥有该特定控件的句柄。通常情况下只有一个UI线程,但在高级应用程序中可能有多个UI线程,并且有理由想要它们。从技术上讲,任何(正常?)线程都可以启动UI消息泵并成为UI线程——并且以后可以关闭消息泵而不再是UI线程。(我认为这不是在线程池线程上尝试的东西。) - Rob Parker
显示剩余12条评论

56

基于Jon Skeet的回答,有时候你希望调用一个委托并等待它执行完之后当前线程再继续。在这种情况下,你需要使用Invoke方法。

在多线程应用程序中,你可能不希望一个线程等待委托执行完毕,特别是当委托执行I/O操作时(这可能会导致委托和线程阻塞)。

在这种情况下,使用BeginInvoke方法会很有用。通过调用该方法,你告诉委托开始执行,同时你的线程可以自由地进行其他并行任务。

虽然使用BeginInvoke会增加代码的复杂性,但是在某些情况下,其改善性能的优势是值得付出复杂性代价的。


32
Control.Invoke()Control.BeginInvoke() 的区别在于,
  • BeginInvoke() 会在 GUI 线程上安排异步操作。当异步操作被安排后,您的代码将继续执行。一段时间之后(您不知道确切的时间),您的异步操作将被执行。
  • Invoke() 将在 GUI 线程上执行您的异步操作并等待直到操作完成。

一个逻辑结论是,您通过 Invoke() 传递的委托可以具有输出参数或返回值,而您通过 BeginInvoke() 传递的委托不能(必须使用 EndInvoke 来检索结果)。


27

只是为了提供一个简短的、可行的例子,以便看到它们之间差异的效果

new Thread(foo).Start();

private void foo()
{
  this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
    (ThreadStart)delegate()
    {
        myTextBox.Text = "bing";
        Thread.Sleep(TimeSpan.FromSeconds(3));
    });
  MessageBox.Show("done");
}

如果使用BeginInvoke,则消息框会同时弹出文本更新。如果使用Invoke,则消息框将在3秒的休眠后弹出。因此,显示了异步(BeginInvoke)和同步(Invoke)调用的效果。


10

仅解释何时和为什么使用Invoke()。

无论是Invoke()还是BeginInvoke(),都会将您指定的代码转移给调度程序线程。

但是,与BeginInvoke()不同的是,Invoke()会阻止您的线程,直到调度程序执行您的代码。如果您需要暂停异步操作,直到用户提供某种反馈,那么您可能需要使用Invoke()。例如,您可以调用Invoke()运行一小段代码,显示一个OK / Cancel对话框。在用户单击按钮并完成您的封送代码后,invoke()方法将返回,并且您可以根据用户的响应进行操作。

请参见Pro WPF in C#第31章


10

Delegate.BeginInvoke() 异步地将委托的调用排队,并立即返回控制。使用 Delegate.BeginInvoke() 时,应在回调方法中调用 Delegate.EndInvoke() 以获取结果。

Delegate.Invoke() 同步地在同一线程中调用委托。

MSDN 文章


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接