【问题标题】:onuserpreferencechanged hang - dealing with multiple forms and mutlipe ui threadsonuserpreferencechanged hang - 处理多个表单和多个 ui 线程
【发布时间】:2013-06-19 12:36:18
【问题描述】:

我认为我的问题类似于:

.NET 4.0 and the dreaded OnUserPreferenceChanged Hang

我也看过了:

http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html#BeginInvokeDance

我已经删除了我们的启动画面。

我还尝试添加建议的代码:Microsoft.Win32.SystemEvents.UserPreferenceChanged += delegate { };到我们的 main() 方法。

我正在寻找有关如何排除故障的一些想法和信息。

对于我们的 main() 方法,我们启动一个 windowmanager 类,它是一个使用 Application.Run 的表单 它只是任务托盘中的一个图标(我们不显示窗口)。

每当我们启动一个对象时,我们都会有一个后台线程来创建一个表单,然后执行 Application.Run(form)

Application.Run(form) 时 form.IsHandleCreated = false。

我使用来自 MysteriousHang 网站的冷冻应用程序。 (我对其进行了修改以保持循环发送更改通知)。

我应该如何处理创建和运行新表单?即使尚未创建其句柄,在后台线程上创建表单是否重要?

【问题讨论】:

  • 当然很重要。句柄在不到一毫秒后创建。在错误的线程上。所以不要那样做。
  • 假设我想同时启动三个表单,每个表单都在各自的线程中。这样做的正确方法是什么?”
  • 我也对术语“UI 线程”感到困惑。从 main() 我们创建了一个 windowManager 对象。这源自表单,但不显示 UI。然后我们调用 Application.Run(windowManager)。 WindowManager 有一个方法来启动一个新的子窗口。此方法创建一个后台线程 (background = true),然后创建一个新的子窗体,并调用 Application.Run(newChildForm)。因此,如果我从窗口管理器启动两个子窗口,这三个线程都是“UI 线程”吗? (main/windowManager 线程,以及为启动子窗口而创建的两个线程)
  • @Derek 正确的方法是不这样做。不要创建 3 个不同的 UI 线程;对所有三种表单使用一个 UI 线程。这才是“正确的方式”。

标签: c# winforms freeze


【解决方案1】:

我也对术语“UI 线程”感到困惑。

UI 线程是一个抽取消息循环的线程。并且在与用户界面对象兼容的模式下运行,它需要是一个 STA,一个单线程单元。这是一个 COM 实现细节,对于非线程安全且需要 STA 的常见 UI 操作(例如 Drag+Drop、剪贴板、OpenFileDialog 和 ActiveX 组件等 shell 对话框)非常重要。

调用 CoInitializeEx() 并选择公寓类型是 CLR 的工作。它由程序中 Main() 入口点上的 [STAThread] 属性引导。存在于创建 UI 对象(如 Winforms 或 WPF 应用程序)的项目中。但不是控制台模式应用程序或服务。对于工作线程,即由您的代码而不是 Windows 创建的线程,单元类型由您传递给 Thread.SetApartmentState() 方法的内容选择。默认是 MTA,错误的味道。线程池线程始终是 MTA,无法更改。

SystemEvents 类有一项令人羡慕的任务,即找出程序中哪个线程是 UI 线程。重要的是它可以在正确的线程上引发事件。它通过使用启发式方法来做到这一点,订阅事件的第一个线程并且是一个STA线程被认为是合适的。

当猜测不准确时,事情就会出错。或者当然,在您尝试创建创建 UI 对象的 多个 线程的情况下,猜测只能对其中一个是正确的。您可能还忘记调用 Thread.SetApartmentState() ,因此它们中的任何一个都不正确。 WPF 更强烈地断言这一点,并在线程不是 STA 时生成异常。

UserPreferenceChanged 事件是一个麻烦制造者,它被您在工具箱上找到的一些控件订阅。他们使用它来知道活动的视觉样式主题已更改,因此他们将使用新的主题颜色重新绘制自己。 一些控件中的事件处理程序的一个重大缺陷是它们假定事件是在正确的线程上引发的,即创建控件对象的同一线程。

不会在您的程序中出现。结果往往是不愉快的,细微的绘画问题是一个小缺陷,僵局肯定是可能的。由于某些原因,使用 Windows+L 锁定工作站并解锁它特别容易导致死锁。在这种情况下会引发 UserPreferenceChanged 事件,因为桌面从安全桌面切换到用户桌面。

监听 UserPreferenceChanged 事件并且使用安全线程实践(使用 Control.BeginInvoke)的控件是 DataGridView、NumericUpDown、DomainUpDown、ToolStrip+MenuStrip 和 ToolStripItem 派生类,可能是 RichTextBox 和ProgressBar(不清楚)。

消息应该很清楚,您正在使用不安全的线程做法,他们可以字节。一般来说,在工作线程上创建 UI 没有任何意义,Winforms 或 WPF 程序的主线程已经非常有能力支持多个窗口。除了避免危险的控制之外,这是您应该努力摆脱问题的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-11
    • 2018-10-27
    • 2020-05-10
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多