在我的 C# Windows 窗体应用程序中,有两个线程:
当任务长时间运行时,它会冻结/卡住整个进程,并且不会引发任何异常或通知。它会挂起应用程序。
内部应用程序仅处理记录(从 SQL 表中选择并插入 Access DB 表)
UI 更新将使用事件操作功能完成。
查找卡住的进程并行任务的附加快照。似乎线程在内部相互等待并导致进程阻塞。与
SystemEvents.UserPreferenceChanged
事件相关的代码位于其中一个堆栈上。
为什么会发生这种情况以及如何解决?
它在
SystemEvents.UserPreferenceChanged
事件上陷入僵局。 这是具有多个线程死锁的窗口的应用程序的标准方式。调用死锁的最佳方法是按 Windows+L 键。 您可以在这篇博文中看到对这个死锁的深入分析。
SystemEvents
类是这里的麻烦制造者,它尝试在程序的 UI 线程上引发事件。这非常重要,UI 不是线程安全的。问题是,您有 two 线程创建了 UI。 SystemEvents
无法猜测哪一个是正确的,它只有 50% 的几率,所以注定会猜错。 如果它最初猜错了程序中的哪个线程是 UI 线程,并且该线程退出了,那么它将是 100% 错误。
这当然使得在工作线程上创建 UI 变得极其危险。 这在技术上是可行的,但是您必须避免使用工具箱中的多个控件。 当
UserPreferenceChanged
事件在错误的线程上引发时,他们不能很好地处理该事件。 肯定导致死锁的是 DataGridView、NumericUpDown、DomainUpDown、ToolStrip+MenuStrip 和 ToolStripItem 派生类。 不确定的(无法足够深入地分析代码)是 RichTextBox 和 ProgressBar。 从你的调用堆栈来看,看起来我应该将 ProgressBar 放在第一组中。
真正的解决方法是不在工作线程上创建 UI。 这从来没有必要,你的程序的 UI 线程已经能够处理任意数量的窗口。