c# - C# WinForms 应用程序中未发生预期的跨线程异常

标签 c# .net multithreading winforms exception

我来自 WPF,是 WinForms 的新手。在调查跨线程情况时,没有发生我预期的跨线程异常。

这是我的情况的基本摘要。有一个名为 label1Label 控件和一个名为 button1Buttonbutton1 的点击事件处理程序基本上如下所示:

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew(()=>
    {
        label1.Text = "Some-Other-New-Text";
    });
}

这并没有像我期望的那样抛出跨线程异常。这是因为WinForms应用程序没有跨线程问题吗?请注意,我已在 Visual Studio 2013 和 Visual Studio 2010 中对此进行了调查。

最佳答案

Windows 窗体在 Windows 消息传递基础结构之上工作。这意味着您对相关控件执行的许多操作实际上委托(delegate)给 Windows 以支持所有控件的正确 native 行为。

Label 不会更改默认实现,并且默认情况下不会在托管代码中缓存文本。这意味着它使用 SetWindowText native 方法来设置当前标签文本(相应地,使用 GetWindowText 来读取它),该方法会发布 WM_SETTEXT到消息循环。真正的更新发生在处理消息循环的线程上,也称为 UI 线程。除非您特意禁止这种调用(当前引用源中的Control.checkForIllegalCrossThreadCall),否则它将起作用。默认情况下,这是根据是否附加调试器来设置的 - 因此您的代码可能在调试时崩溃,但可以在调试器之外工作,因为 SetWindowText 恰好是线程安全的。 Text 属性的其他部分可能是也可能不是线程安全的,但如果幸运的话,一切都会正常工作。

您可以显式地将 Control.CheckForIllegalCrossThreadCall 设置为 true,我建议您这样做。从多个线程访问任何资源很容易出现难以调试的问题,并且将 UI 上需要完成的任何工作编码到 UI 线程......无论如何都是 UI 线程的工作。

仅从 UI 线程操作 UI 会给您带来非常重要的好处:

  • 可预测性和可靠性 - 事情往往会按照某些可靠的顺序发生。如果我有一百个线程设置两个不同控件的Text,则将 UI 更新委托(delegate)给 UI 线程将确保这两个控件始终具有一致的值,而直接从后台线程更新往往会导致“随机”交错更新。在实际应用程序中,这可能会导致困惑并且难以发现错误。请注意,这不是绝对的 - 任何 await/Application.DoEvents 可能会也可能不会破坏这一点。但即使在这种情况下,您也有明确定义的同步点,而不是抢占式多任务处理。
  • 多线程很难。您使用的大多数东西都不是线程安全的,甚至据称线程安全的操作在 MT 场景中运行时也可能存在多线程错误或简单的复杂行为。即使像按顺序更新两个 bool 值这样简单的事情也会变得危险,并且可能会引入难以调试的错误。最好的选择是尽可能多地保持线程仿射。您通常会发现可以将线程之间的所有接口(interface)限制到代码的一小部分,这使得它们更容易测试和调试。
  • 无论如何,当你已经将“工作”与“UI”分开时,这真的很便宜。这本身就是一种非常方便的设计实践。

顺便说一句,您通常希望等待您分拆的任务,或显式处理异常。如果启用 Control.CheckForIllegalCrossThreadCall ,标签将不再更新,但也不会显示异常 - 默认情况下,线程池线程现在会忽略未处理的异常(自 .NET 4.5.2 IIRC 起)。 await 会将任何异常(和返回值)编码回 UI 线程。

关于c# - C# WinForms 应用程序中未发生预期的跨线程异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36860555/

相关文章:

python - 线程停止应用程序

javascript - 在 C# 中使用 Selenium WebDriver 执行 JavaScript

c# - 从远程客户端接收文件

c - 并行执行时间

multithreading - C++0x 线程和套接字

c# - 什么会导致添加到哈希表的键为空?

c# - 无法打开登录请求的数据库 ""。登录失败。用户 'sa' 登录失败

c# - RavenDB Collection "in"集合查询

c# - 高级套接字函数

.net - 为什么我的 AWS Lambda 在 ARM64 架构中运行时崩溃?