18

What is a UI thread? Is there only one UI thread in a .NET application?

3 Answers3

14

(Simplification ahead)

A UI thread is a Single Threading Apartment thread that is used to create various user interface objects (in Winforms, this means Controls). By convention and rule, a Control may only be accessed from within the thread that was used to create it; doing otherwise can and will produce unexpected results, from visual oddities all the way to a crash.

Unless you explicitly create more, there is only one UI thread within a Windows Forms application. While you can create another thread and start a message loop, there are very few reasons why you'd want to do this, and two different UI threads cannot "talk" to each other any more than any other thread can talk to a UI thread.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 2
    +1 for mentioning that you can do this, but that it's rarely the way things should be done... – Reed Copsey May 04 '10 at 19:26
  • So what about when you do new Thread().Start() inside a STAThread? –  May 04 '10 at 19:30
  • @Griever: That starts a new thread, but it's completely separate from the UI thread. Doing a new Thread start anywhere creates a new, unrelated thread. – Reed Copsey May 04 '10 at 19:30
  • @Reed: You mean, that if I create a control inside Thread.Start, that control won't have any message pump attached to it? –  May 04 '10 at 19:39
  • @Griever: Yes. And if you try to add it to a different form, you'll get errors. It's, in general, a bad idea. (You need to create the thread as an STA thread, then also add a message pump to the thread, then create your form. That lets you start a new UI thread, but it's really typically a bad idea - there are very few legitimate reasons to do that - since you're nearly always better just pushing your "work" onto background threads and leaving a single UI thread in place) – Reed Copsey May 04 '10 at 19:46
10

A UI thread has a number of characteristics that make it special:

  • Windows has a message queue associated with the thread. This happens as soon as the very first window gets created on the thread.
  • The thread runs a message loop, allowing Windows to dispatch messages to the windows. The message loop gets created as soon as you call Application.Run().
  • COM was initialized on the thread, requesting a single-threaded apartment. An STA is necessary to allow many Windows features to work properly, features that are not thread-safe by design. COM ensures that these features are always called in a thread-safe manner, marshaling the call from a worker thread to the STA thread as needed. Examples of these features are Drag+Drop, the clipboard, the shell dialogs (OpenFileDialog etc), ActiveX controls like WebBrowser, window hooks set by SetWindowsHookEx, accessibility providers such as used by a screen reader, UI automation providers. All external code, none of it thread-safe.
  • The thread never blocks on any operation, it stays responsive so it can dispatch Windows messages as required to keep the user interface responsive and COM marshaling requests flowing. Making calls to WaitHandle.WaitAny() for example are explicitly forbidden and generate an exception. The CLR has specific support for WaitOne() and lock, pumping an internal message loop to avoid deadlock.

The startup thread of a process is almost always selected as the UI thread, although that's not a hard requirement. The STA state is selected by the [STAThread] attribute on the Main() method.

You can create a UI thread by ensuring that the above requirements are met. That could look like this in a Winforms app:

    var ui = new Thread(() => { Application.Run(new Form2()); });
    ui.SetApartmentState(ApartmentState.STA);
    ui.Start();

That creates a second window, running on its own UI thread. One typical problem you have with this arrangement is that you've now got two separate windows, they are not associated with each other at all. The 2nd window cannot be owned by the 1st, it has its own Z-order independent of the 1st. Difficult to deal with by the user. The SystemEvents.UserPreferenceChanged event is notable, it will inevitably fire its event on the wrong thread and that is apt to cause deadlock. Lots of WinForms controls subscribe it. Except in rare circumstances, like a splash screen, this doesn't improve the user interface at all.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

EDITED for correctness:

There is one UI thread per active APPLICATION in Windows Forms and similar concept for WPF.

ie: When you start the app, there is one thread, it becomes a UI thread when Application.Run(new Form1()); is called.

If you try to do Application.Run(new Form2()); at runtime you will get "System.InvalidOperationException: Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead."

If you really needed two seperate forms to not share the same thread, you would need to create a new thread, then call Application.Run(new MyForm()) etc etc. This is not common.

David
  • 2,785
  • 1
  • 16
  • 8
  • 1
    This is not true. Different forms, in Windows Forms, all use the **same** thread. The message pump makes them all responsive. – Reed Copsey May 04 '10 at 19:25
  • I had it reversed, modal dialogs require a new thread, modeless dialogs do not *require* a new thread. The point still stands that normal threads can become ui threads when a new form is launched from them. – David May 04 '10 at 19:29
  • This is still untrue. "Launching a form" from a new thread, without special care to start a message loop, will cause all sorts of problems, including (in most cases) crashing due to lack of STA by default. – Reed Copsey May 04 '10 at 19:31
  • You may be right. There was a case in the past where I tried to show a form and at runtime got an error telling me I could not have two forms open on the same ui thread, I am trying to track that down to comment here, but I haven't found it yet – David May 04 '10 at 19:33
  • Just edited my post, I was confusing Application.Run with Form.Show. – David May 04 '10 at 19:47