调用 MessageBox 和开始调用 MessageBox 有什么区别?

12

在一个表单中,比较

BeginInvoke (new Action (() => {
    MessageBox.Show ());
}));

Invoke (new Action (() => {
    MessageBox.Show ());
}));

这两种方法有何区别?在什么情况下应该使用其中一种?MessageBox的消息泵如何影响它们的行为?

我进行了一些测试,发现两种方法都会阻塞UI。

唯一的区别是Invoke会立即被调用,而BeginInvoke需要(非常短暂的)时间才能运行代码。这是可以预料的。


给所有回答者:抱歉,目前没有投票机会,明天会点赞的 :) - mafu
5个回答

16

BeginInvoke会异步调用委托,立即返回并将委托排队以独立于当前线程执行。

Invoke会同步调用委托,阻塞调用线程直到委托完成。

为了看出区别,请尝试以下代码:

BeginInvoke(new Action(()=>Console.WriteLine("BeginInvoke")));
Console.WriteLine("AfterBeginInvokeCalled");

Invoke(new Action(()=>Console.WriteLine("Invoke")));
Console.WriteLine("AfterInvokeCalled");

你应该看到类似以下的输出,其中"BeginInvoke"文本由于异步执行而延迟:

AfterBeginInvokeCalled
Invoke
AfterInvokeCalled
BeginInvoke

关于你观察到的行为,只有调用委托是同步或异步的; 方法的内容可能会导致调用线程停止或阻塞UI。在显示消息框的情况下,无论是否使用BeginInvoke延迟委托,一旦委托被调用,UI将会被阻塞直到消息框被关闭。


14

事实上,Simon说的没错。

BeginInvoke 就像是给UI线程发送一条消息,告诉它:“有机会就执行这个操作。”

Invoke 则是直接告诉UI线程:“现在就执行这个操作。我等你。”

澄清一下:即使你告诉UI线程“现在就执行这个操作”,也并不意味着你能够控制UI线程放弃它正在做的任务。基本上,上面语句中关键的词是“我等你”。

问题在于,在你的代码示例中,你要发送到UI线程的消息是:调用 MessageBox.Show。猜猜看?无论哪种方法,这都会阻塞UI线程。

如果你想体验 BeginInvoke 异步行为,需要从一个单独的线程中调用它,在代码中使用 BeginInvoke 后加一个断点,观察当消息框出现时(UI被阻塞),断点是否会被命中。如果你调用 Invoke,代码将不会继续执行直到用户关闭消息框。


1
这正是我正在寻找的解释 :) - mafu
1
很好的解释。只想指出BeginInvoke和Invoke都表示“尽快执行此操作”,但Invoke还添加了“我会等到你完成”。Invoke不能加快事情的进程,正如你所建议的那样(“立即执行此操作”)。 - Allon Guralnek
@Allon:是的,我想你如何解释这些话有点主观。问题在于,在现实生活中,你也不能立刻让某人做某事--但你可以告诉他们立即去做,并站在那里双手交叉,踢着脚直到他们真正去做。对我来说,这就是Invoke的意图--你说得对,它不像你可以强制它中止当前执行的任何代码;但你有效地了,“现在就这样做。” - Dan Tao
“现在”意味着时间上由执行者掌控,而不是请求者掌控,我认为这是具有误导性的。您编辑的澄清只是消除了没有关于时间保证的可能性,但仍然可能会误解对InvokeBeginInvoke的两个同时调用,因为它更“紧急”,所以可能具有更高的优先级,但实际情况并非如此。“做这件事,我有工作要做,所以在完成后回电给我”和“做这件事,我会一直等到你完成”似乎更准确地描述了人的行为方式。尽管如此,还是点赞。 - Allon Guralnek

3

BeginInvoke是异步的...这意味着调用线程不会等待被调用方法返回。

所以,好吧,对话框总是会冻结GUI。但是,开始调用和调用之间的区别现在应该很清楚了:

Invoke等待被调用方法返回,而BeginInvoke不会等待。


我不同意。请看修改后的问题。---编辑:啊,是的,但是MessageBoxes总是模态的吗? - mafu
@mafutcrt:我认为你混淆了两件事情。MessageBox仍然会阻塞UI,但是通过BeginInvoke调用lambda方法的实际行为是异步的。仅仅因为该调用是异步的,并不意味着该调用的结果也是异步的。 - Jeff Yates
@Jeff:是的,没错。我建议@Simon在他的回答中加入这个解释,以使其更清晰明了。 - mafu

2
大多数答案在技术上是正确的,但它们没有问一个显而易见的问题。
首先,为什么要在Invoke/BeginOnvoke中包装MessageBox()调用?
在这种情况下使用BeginInvoke或Invoke没有任何好处,正如Jeff所解释的那样。
听起来你正在混淆在多线程情况下在Windows窗体/控件上使用Invoke/BeginInvoke和在委托实例(即异步编程模型)上使用Invoke/BeginInvoke。
由于名称显然相同,这很容易发生,但你会使用它们以及它们的行为是不同的。
书籍CLR Via C#对Invoke/BeginInvoke的两种类型都有很好的解释。

1
对于 MessageBox.Show,问题大多数情况下是无关紧要的。
唯一的区别在于,使用 BeginInvoke 时,调用线程本身不会阻塞,因此它可以继续执行其他操作(清理、进一步处理等)。
UI 线程显然会被阻塞,因为有一个模态窗口弹出,等待用户输入以关闭它。

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