长期延迟的更新
我接受 MUG4N 对这个问题的回答,也想回应一些对它提出的批评。
ChrisF 说:
...你不能直接从后台线程进行 UI 调用。
这是一个笼统的说法,并不完全正确。让我指出一些事实:
如果设置
Control.CheckForIllegalCrossThreadCalls = false
,实际上你可以随心所欲地进行 UI 调用。我听到你在说“啊!”、“永远不要那样做!”是的,是的——但为什么?答案是:因为有时这会破坏内存。System.Windows.Forms
中的控件类不是线程安全的,因此有时从后台线程更新它们可能会破坏内存。但如果这只是有时发生而不是总是,那么这告诉我们:它并不是调用 UI 代码本身,而是潜在的不安全的 UI 代码交错导致异常。为了加强第1点,考虑这个:从后台线程调用 UI 代码的“安全”方式是使用
Control.Invoke
或Control.BeginInvoke
,对吧?但这也是一个 UI 调用;这只是我们应该从非 GUI 线程更新 GUI 时要进行的 UI 调用。我的意思是,显然,仅仅是从外部线程调用Control
对象上的“任何”方法并不会导致混乱(如果是这样的话,我们甚至无法调用Invoke
,完全被卡住了)。再次强调,不安全的分离 UI 调用同时发生可能导致破坏。记住以上两点,问问自己:为什么从非 GUI 线程调用
MessageBox.Show
是不安全的?会创建并显示一个完全独立的Form
;它的属性不以任何方式与任何其他现有的 GUI 对象交互;实际上,除了从调用线程访问其DialogResult
属性(仅通过Show
方法的返回值),它不能以任何方式在任何地方被访问。
接下来,Conrad Albrecht 说:
...鉴于 Dan 引用的主题声称 Show() 设置了自己的消息泵,(尽管没有被证实,但我无法反驳)...
这是一个完全公正的观点(虽然我个人认为 Jared Par 的声誉足以让我不太倾向于怀疑他说的话)。无论如何,通过Reflector查看MessageBox.Show
方法,可以看到以下代码片段:
Application.BeginModalMessageLoop();
try
{
result = Win32ToDialogResult(SafeNativeMethods.MessageBox(new HandleRef(owner, zero), text, caption, type));
}
finally
{
Application.EndModalMessageLoop();
UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}
进一步查看Application.BeginModalMessageLoop
方法会发现以下内容:
ThreadContext.FromCurrent().BeginModalMessageLoop(null);
而这个ThreadContext.FromCurrent
,反过来:
// [Reflector shows that currentThreadContext is a ThreadStatic member. -Dan]
if (currentThreadContext == null)
{
currentThreadContext = new Application.ThreadContext();
}
return currentThreadContext;
我对这些较低级别的Windows结构不太了解,所以无法完全理解这段代码,但是这似乎证明了Jared在我早期评论中提到的答案中所说的正是事实(对于好奇的读者:MessageBox.Show()自动将线程传递给UI线程吗?)。
所以,是的。我完全同意MUG4N的观点。
(如果有人能够令人信服地证明我在这里还是错误的,请说出来。尽管我感觉我已经为为什么我相信MUG4N正确提供了充分的理由,但我显然不是100%确定的。)
原始问题
通常,您只想通知用户发生了某些事情,但实际上并不需要他们的任何输入。在这种常见情况下,我有时会看到像这样的代码:
MessageBox.Show("Something has occurred", "Something", MessageBoxButtons.OK);
众所周知,这段代码会导致一个小弹窗出现,只有一个确定按钮。问题是:此代码会阻塞(UI线程)。但在绝大多数情况下,如果你只有一个确定按钮,似乎很少需要阻塞。(阻塞的目的通常不是从用户那里接收一些输入吗?而在这种典型情况下,如果用户的唯一选择是“确定”,那么阻塞就显得相当无意义了,是吗?)
显然,我可以编写自己的小表单,实现与MessageBox.Show
基本相同的功能,只是不返回任何内容(没有DialogResult
),也不阻塞。但我想知道是否已经存在类似于此的东西,我不知道。