我也对术语“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 程序的主线程已经非常有能力支持多个窗口。除了避免危险的控制之外,这是您应该努力摆脱问题的方法。