久违的更新
我接受 MUG4N 对这个问题的回答,我也想回应一些针对它提出的批评。
ChrisF 说:
...您不能直接从后台线程进行 UI 调用。
这是一个笼统的陈述,并非 100% 正确。让我指出几个事实:
如果您设置
Control.CheckForIllegalCrossThreadCalls = false
. “啊!” 我听到你说。“永远不要那样做!” 是的,是的——但为什么呢?答案:因为有时这会破坏内存。中的控制类
System.Windows.Forms
未编写为线程安全的,因此有时从后台线程更新它们可能会损坏内存。但是,如果这种情况只是偶尔发生而不总是发生,那么这告诉我的不是 UI 代码本身的调用,而是可能导致异常的 UI 代码的潜在不安全冲突。为了强化第 1 点,考虑一下:从后台线程调用 UI 代码的“安全”方式是使用
Control.Invoke
orControl.BeginInvoke
,对吗?但这是一个 UI 调用;如果我们从非 GUI 线程更新 GUI,这只是我们应该进行的UI 调用。我的意思是,很明显,它不仅仅是Control
从外部线程调用对象的“任何”方法,这会导致混乱(如果是这种情况,那么我们甚至无法调用Invoke
,我们会完全陷入困境) . 同样,不能安全地同时发生的单独 UI 调用的潜在冲突将证明是破坏性的。牢记以上两点,问问自己:为什么
MessageBox.Show
从非 GUI 线程调用会不安全?一个完全独立Form
的被创建和显示;它的属性不以任何方式与任何其他现有的 GUI 对象交互;事实上,它不能以任何方式在任何地方访问,除了一种:从调用线程访问它的属性(并且只能通过方法的返回值)。DialogResult
Show
一起前进。康拉德·阿尔布雷希特说:
...鉴于 Show() 在 Dan 的 ref'd 主题中设置了自己的消息泵的断言,(这没有得到证实,但我无法反驳)...
这是一个完全公平的观点(尽管我个人对 Jared Par 有足够的尊重,以至于我通常不会怀疑他所说的话)。无论如何,通过ReflectorMessageBox.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 在我在旧评论中引用的答案中所说的话(对于好奇的读者:Does MessageBox. Show() 自动编组到 UI 线程?)。
是的。在这一点上,我完全同意 MUG4N。
(如果有人可以令人信服地争辩说我在这里仍然是错误的,请说出来。虽然我觉得我已经为为什么我认为 MUG4N 是正确的做了一个很好的理由,但我显然不是 100% 确定的。)
原始问题
通常你只是想通知用户发生了一些事情,但实际上不需要他们的任何输入。在这种常见情况下,我有时会看到如下代码:
MessageBox.Show("Something has occurred", "Something", MessageBoxButtons.OK);
众所周知,这段代码会导致出现一个只有一个确定按钮的小弹出窗口。现在事情是这样的:这段代码块(UI 线程)。但在我看来,在绝大多数情况下,如果您只有一个确定按钮,则几乎不需要阻止。(阻塞的目的通常不是为了接收用户的一些输入吗?如果用户的唯一选择是“OK”,在这种典型情况下,阻塞不是毫无意义吗?)
显然,我可以编写我自己的小表单,它基本上完全MessageBox.Show
可以做,除了它什么都不返回(no DialogResult
)并且不阻塞。但我只是想知道这样的事情是否已经存在而我不知道。