【问题标题】:'Cross-thread operation not valid' does not kill WinForms application (and it should)“跨线程操作无效”不会杀死 WinForms 应用程序(它应该)
【发布时间】:2010-12-02 03:23:35
【问题描述】:

更新:这是一篇 MSDN 文章 How to: Make Thread-Safe Calls to Windows Forms Controls。它指出:

.NET Framework 可帮助您检测 当您访问控件时 以一种不是线程安全的方式。 当你运行你的应用程序时 在调试器中,以及其他线程 比创建控件的那个 试图调用该控件, 调试器引发 InvalidOperationException 与 消息,“控制控制名称 从线程以外的线程访问 创建它的线程。”

此异常可靠地发生在 调试,并且在某些情况下 情况下,在运行时。

我之前的经验是,异常也是在运行时抛出的。

感谢 Spence 为我指明了正确的方向。


我在 WinForms 应用程序中有一个非常常见的错误:后台线程直接访问 UI 控件而不是使用 Control.BeginInvoke()。

我的问题如下:我看到 InvalidOperationException“跨线程操作无效:控制 'uxCheckStatus' 从创建它的线程以外的线程访问。”在后台线程的调试器中,但随后它在 WinForms 内部的某个地方被吞没了。

我希望它会杀死后台线程和整个应用程序。

此外,触发它的代码uxCheckStatus.Text = "success"; 有时会在抛出异常期间/之后执行,即标签文本显示为“成功”!我基本上迷路了。还有其他人遇到这种行为吗?

我在带有 1 个按钮的全新 WinForms 解决方案上重现它,两者都使用 ThreadPool 和 Thread 作为邪恶的后台线程。

如果我在后台线程上抛出一个新的 InvalidOperationException(),它会杀死应用程序。所以我唯一的猜测是 WinForms 在某个地方处理了这个特定的异常,但我在网络上找不到对这种行为的引用。

我运行 .NET 3.5,VS 2008。

【问题讨论】:

    标签: winforms exception


    【解决方案1】:

    我不知道有关 WinForms 如何/为何处理此异常的任何具体文档。但是,如果您想在发生这种情况时优雅地关闭您的应用程序,请考虑使用global exception handler

    【讨论】:

    • Application.ThreadException 未被调用。 AppDomain.CurrentDomain.UnhandledException 是,理论上我可以从中关闭应用程序。
    【解决方案2】:

    这是设计使然。这是一个编码错误,而不是运行时错误。 Winforms 的开发人员决定从 .Net 2 开始,他们将实现代码来检查跨线程代码,然后触发 MDA。 MDA 也不例外,它是对设计不良代码的检查。

    此外,当您的代码处于发布模式时,MDA 不会触发,它只是偶尔随机失败,MDA 是在测试时帮助您,而不是在发布期间。

    我假设您正在重写一个写得很糟糕的应用程序来调用线程,并且您希望异常会为您捕获,我有一个建议:

    更新您的 GUI 事件处理程序,使其看起来像这样:

    public void button_clicked_handler(object sender, EventArgs e)
    {
       if(this.InvokeRequired)
    {   
       this.Invoke(delegateToThisMethod)
    }
    else
    {
       //perform method
    }
    

    此模式将捕获您的所有方法并使其无缝用于跨线程操作。如果线程直接访问代码,这就有点困难了。您可以通过重命名控件来解决此问题,然后为控件创建一个属性,然后您可以在其中应用调用模式。我以前必须这样做才能解决一些非常时髦的跨线程问题。

    编辑: 只是想澄清一下,检查当前线程和 Win Forms 线程的同步上下文是一项代价高昂的操作。因此,他们实现 MDA 的原因是您可以在调试中找到它,但您的发布模式代码不会在每次访问 Windows 窗体控件的每个属性或方法时变慢。

    【讨论】:

    • 什么是“MDA”?你有没有提到“按设计”?让我重复一遍:我知道我应该使用“Control.BeginInvoke”。而且我认为当我忘记使用它时,应用程序应该“快速失败”,而不是吞下异常。
    • 好的,在你提到调试模式之后,我在MSDN上找到了参考,谢谢。
    • 我知道它被埋在 MSDN 的某个地方。将信息放入问题中做得很好,很多人没有意识到 SO 被设计为有用答案的存储库,这样的维护使这个网站变得很棒。祝你调试好运,没有什么比阅读别人的代码更重要了:)
    • MDA:托管调试助手。 MDA 是在附加调试器时在代码方面发生错误时通知您的东西,但在发布模式下不会发生。基本上,当你做了你不应该做的事情时,他们会检测 .Net 并开火。
    猜你喜欢
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    相关资源
    最近更新 更多