views:

167

answers:

3

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

A: 

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
This is not true. Different forms, in Windows Forms, all use the **same** thread. The message pump makes them all responsive.
Reed Copsey
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
@David: 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
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
Just edited my post, I was confusing Application.Run with Form.Show.
David
+7  A: 

(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
+1 for mentioning that you can do this, but that it's rarely the way things should be done...
Reed Copsey
So what about when you do new Thread().Start() inside a STAThread?
Griever
@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
@Reed: You mean, that if I create a control inside Thread.Start, that control won't have any message pump attached to it?
Griever
@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
+2  A: 

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).
  • 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 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 another UI thread by ensuring that the above requirements are met. That could look like this in a boilerplate WF app:

    private void button1_Click(object sender, EventArgs e) {
        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. Note that closing the startup form doesn't terminate the app. 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. Except in rare circumstances, this doesn't improve the user interface at all.

Hans Passant
I like your detailed answer.
Griever