UI freezes when using SystemEvents.UserPreferenceC

2019-09-06 19:24发布

问题:

In my C# Windows Forms application there are two threads:

  1. Main Thread (Program.cs)
  2. WorkerClass Thread (STA-Apartment).

When there is long running Task, it freeze/stuck the entire process and No exception or notification fired..it hangs application.

Internally applications doing processing of records only (selection from SQL tables & inserting in Access DB tables)

UI updates will be done using event Action feature.

Find attached snap for stuck process parallel tasks. Seems like threads internally wait for each other and get process blocked. Code related to the SystemEvents.UserPreferenceChanged event is on one of the stacks.

Why does this happen and how can I resolve it?

回答1:

It deadlocks on a SystemEvents.UserPreferenceChanged event. This is the standard way an app with windows on more than one thread deadlocks. Best way to invoke the deadlock is to press the Windows+L key. You can see this deadlock analyzed in depth in this blog post.

The SystemEvents class is the troublemaker here, it makes an attempt to raise it events on the UI thread of a program. Which is very important, UI isn't thread-safe. Trouble is, you've got two threads that created UI. SystemEvents is incapable of guessing which one is right, it only has 50% odds at it so is doomed to get it wrong. If it initially guessed wrong at which thread in your program is the UI thread, and that thread exited, then it will be 100% wrong.

This makes creating UI on a worker thread exceedingly hazardous of course. It is technically possible, you'll however have to avoid using several controls from the toolbox. They don't handle the UserPreferenceChanged event well when it is raised on the wrong thread. The ones that definitely cause deadlock are DataGridView, NumericUpDown, DomainUpDown, ToolStrip+MenuStrip and the ToolStripItem derived classes. The ones that are iffy (can't analyze the code deep enough) are RichTextBox and ProgressBar. Looks like I ought to put ProgressBar in the first set, judging from your callstacks.

The true cure is to not create UI on a worker thread. It is never necessary, the UI thread of your program is already capable of handling any number of windows.