I think my problem is similar to:
.NET 4.0 and the dreaded OnUserPreferenceChanged Hang
I have also looked through:
http://ikriv.com/en/prog/info/dotnet/
I'm also confused about the terminology "UI thread".
A UI thread is a thread that pumps a message loop. And operates in a mode that's compatible with user interface objects, it needs to be an STA, a Single Threaded Apartment. That's a COM implementation detail that matters a great deal to common UI operations that are not thread-safe and require an STA, like Drag+Drop, the Clipboard, shell dialogs like OpenFileDialog and ActiveX components.
It is the CLR's job to call CoInitializeEx() and select the apartment type. It does so guided by the [STAThread] attribute on the Main() entrypoint in your program. Present in projects that create UI objects like a Winforms or WPF app. But not a console mode app or service. For a worker thread, in other words a thread that was created by your code instead of Windows, the apartment type is selected by what you passed to Thread.SetApartmentState() method. The default is MTA, the wrong flavor. A threadpool thread is always MTA, that cannot be changed.
The SystemEvents class has the unenviable task of figuring out which thread is the UI thread in your program. Important so it can raise events on the correct thread. It does so by using a heuristic, the first thread that subscribes an event and is an STA thread is considered suitable.
Things go wrong when that guess wasn't accurate. Or certainly in your case where you try to create multiple threads that create UI objects, the guess can only ever be correct for one of them. You probably also forgot to call Thread.SetApartmentState() so it won't be correct for any of them. WPF more strongly asserts this and will generate an exception when the thread isn't STA.
The UserPreferenceChanged event is a trouble-maker, it is subscribed by some of the controls you find on the toolbox. They use it to know that the active visual style theme was changed so they'll repaint themselves, using the new theme colors. A significant flaw in the event handlers in some of these controls is that they assume that the event is raised on the correct thread, the same thread that created the control object.
This will not be the case in your program. The outcome tends to be unpleasant, subtle painting problems are a minor flaw, deadlock is certainly possible. For some reason, locking the work station with Windows+L and unlocking it is particularly prone to causing deadlock. The UserPreferenceChanged event is raised in that case because of the desktop switch from the secure desktop the user's desktop.
The controls that listen to the UserPreferenceChanged event and do not use safe threading practices (using Control.BeginInvoke) are DataGridView, NumericUpDown, DomainUpDown, ToolStrip+MenuStrip and the ToolStripItem derived classes, possibly RichTextBox and ProgressBar (unclear).
The message ought to be clear, you are using unsafe threading practices and they can byte. There in general is never any point to creating UI on a worker thread, the main thread of a Winforms or WPF program is already quite capable of supporting multiple windows. Short from avoiding the dangerous controls, this is what you should strive for to get rid of the problem.