为什么在这里使用BeginInvoke?

4

我正在研究别人的代码,对于任何与多线程有关的内容都没有太多经验。 我发现了这一行代码:

BeginInvoke((MethodInvoker)delegate() { btnCalibrate.PerformClick(); });

我想知道为什么要这样做,而不是只用这个就可以了:btnCalibrate.PerformClick();

谢谢您的答案。

2个回答

6
因为如果此代码在与创建GUI中的按钮不同的线程中运行,将会抛出异常。后台线程不能直接调用GUI上的方法,因为GUI不是线程安全的。
当然,这只是使用BeginInvoke的合理原因!但是,经常会发现包含咒语或魔法咒语的代码,这些代码没有任何好的原因,因为作者看到另一个例子是那样做的,所以认为在所有情况下都必须这样做。如果您正在处理的代码是单线程的,则无需使用BeginInvoke。
特别是,Windows Forms本身坚定地是单线程的。所有窗口和控件上的所有操作都发生在同一线程上,并且它们的所有事件都在该同一线程上触发。使用一个GUI线程通过连续在线程上运行并从队列中读取消息的消息循环来分配。 BeginInvoke的目的最终是向该队列发布一条消息,有效地表示“在有空闲时,请运行此代码块”。

请原谅我的无知,但是btnCalibrate存在于“Form1”上,上面的代码也存在于“Form1”上。上面的代码存在于名为void wm_OnWiimoteChanged的函数中,在Form1.cs文件中,而btnCalibrate_performClick则存在于同一文件中。那么,“不同的线程”从哪里来? - Aishwar
如果您能够提供一些关于这个主题的文章,那将不胜感激! :) - Aishwar
1
@aip.cd.aish:我喜欢这篇文章:http://msdn.microsoft.com/en-us/magazine/cc188732.aspx啊...回忆... - codekaizen
1
糟糕...文章错了...实际上是Ian Griffiths的:http://msdn.microsoft.com/en-us/magazine/cc300429.aspx不过两篇文章看起来都不错。 - codekaizen
@codekaizen:感谢提供的链接。我会仔细阅读它们。我还是有一个疑问——如果要使用BeginInvoke从不同的线程访问另一个线程中的信息,那就必须先有两个线程。但在这个应用程序中——上面的所有内容只在一个窗体中。窗体是否有两个线程? - Aishwar
@Earwicker:谢谢你的回答。据我所见,我认为这个应用程序是单线程的,但如果我发现了其他情况,这些知识肯定会有所帮助。 - Aishwar

1

更进一步解释@Earwicker的观点 - Control.BeginInvoke是一种将调用从一个线程转移到拥有Control实例的线程的方法。在后台,它使用Win32消息通过Win32函数PostMessage将调用传递到拥有线程上的PerformClick方法。这是由于Win32 Windows以及WinForms控件只能从创建它的线程安全访问,通常是运行GUI的单个线程。

之所以使用BeginInvoke而不是Invoke是因为前者使用PostMessage并立即返回,而后者使用SendMessage(在底层)并需要等待PostMessage并等待来自GUI线程的回复。这可能会延迟调用线程甚至锁定应用程序,因此在这里BeginInvoke是更好的选择。


Invoke不使用SendMessage。它也使用PostMessage,但在返回之前等待结果。 - Andy
@Andy - 好的。在实践中,您使用BeginInvoke/Invoke的方式类似于PostMessage/SendMessage,但SendMessage(跳过消息队列并直接进入WinProc)与PostMessage/Wait的行为不同,足以使这种区别得以保留。我之前从未注意到,因为对我的用途没有实际差异。 - codekaizen

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