4

All,

I have been using threads for a while in C# but I am still a bit confused about what a thread's apartment state really means. I know that WinForms must always use the STA apartment state (as opposed to MTA) but I am still not clear about what apartment states are all about.

GrandMasterFlush
  • 6,269
  • 19
  • 81
  • 104
koumides
  • 2,464
  • 5
  • 34
  • 54

2 Answers2

11

COM had very lofty goals. One of them was that threading was a programming detail that was very hard to get right and should be managed by support libraries. Very unlike .NET where it is entirely up to you to use classes that are not thread-safe in a thread-safe manner.

This works through the registry, a COM coclass publishes the ThreadingModel registry key that says what kind of threading it supports. By far most of them use "Apartment", a somewhat unclear way to say "I don't support multi-threading". Which is a signal for COM to make sure that all of the methods are called in a thread-safe manner. If your program calls a method from a worker thread then COM takes care of marshaling the call from the worker to the thread that created the instance. Thus automatically ensuring that the server is used in a thread-safe manner. Not unlike the way Control.Invoke and Dispatcher.Invoke works, but completely automatically.

This is wonderful magic of course, but your program does have to co-operate a bit. COM cannot marshal calls like that without your help. When your program creates a thread, it must call CoInitialize() to tell the COM infrastructure that you want to participate in COM calls. At that time, you have to tell it what kind of thread you created. There are two, distinguished by the 'apartment' type. There is STA (Single Threaded Apartment) and MTA (Multiple). An STA thread is a hospitable home to COM components that don't support threading. An MTA thread is not.

There's a price tag attached with the kind of apartment though. When you create an STA then you have to follow STA rules. Which are a bit draconic:

  • You must pump a Windows message loop
  • You can never block the thread

The message loop is the mechanism by which COM marshals calls from one thread to another. The never-block rule is required to prevent deadlock. While draconic, it is however the way a UI thread of a program works. This is not a coincidence.

There's also a price tag attached to creating an apartment threaded COM object on an MTA thread. Such a thread is not a good home, it doesn't follow the STA rules. COM cannot be helpful and get the method calls marshaled. COM steps in and actually creates its own STA thread to give the object a hospitable home. Nice, but not cheap since that burns up a thread and every method call is marshaled, that adds a lot of overhead to every single call.

COM threading support is quite nice, it takes care of everything 98% of the time without having to do anything special. It is however the 2% that can give you an enormous migraine, there's very little you can do to whack it. It also scales very poorly, probably the biggest reason that .NET doesn't have anything similar.

While COM might appear dead, apartments are still a very big deal in Windows programming. The .NET framework has explicit support for them. Calling Thread.Join() or Monitor.Enter() on an STA thread for example, explicitly verboten by COM, makes the CLR pump a message loop. Other artifacts are the [STAThread] attribute you see on the Main() method of a GUI app and Thread.SetApartmentState(), the ways to get the CLR call CoInitialize() the correct way. GUI features like the clipboard, drag and drop and the shell dialogs (like OpenFileDialog) explicitly require STA to work. A thread that creates any windows should always be an STA thread.

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

This blog post on The Old New Thing by Raymond Chen gives some of the history behind the STA and MTA threading models:

User interface code + multi-threaded apartment = death

[ . . . ] COM grew two personalities, one focused on the GUI customers and another focused on the non-GUI customers. For the non-GUI customers, additional functionality such as multi-threaded apartments were added, and since the customers didn't do GUI stuff, multi-threaded apartments weren't burdened by the GUI rules. They didn't post messages to communicate with each other; they used kernel objects and WaitForSingleObject. Everybody wins, right?

Well, yes, everybody wins, but you have to know what side your bread is buttered on. If you initialize a GUI thread as a multi-threaded apartment, you have violated the assumptions under which multi-threaded apartments were invented! [ . . . ]

Justin
  • 6,611
  • 3
  • 36
  • 57
  • still...what is the difference between the two models? Maybe with an example? – koumides Mar 02 '11 at 15:09
  • @koumides - There isn't much difference in C# code, except for changing the attribute on the entry point (`[STAThread]` vs `[MTAThread]`). The UI thread must be STA. If you get it wrong, you may observe buggy behavior, crashes, deadlocks, etc. I don't really know what sort of example you're looking for. If you are coding directly to Win32 without using WinForms (using C, C++, or C# with P/Invoke), then, yeah, there will be differences. – Justin Mar 02 '11 at 15:29
  • @koumides - In short, developing in C#, I don't really know the details, nor do I have to. See http://stackoverflow.com/q/127188/250918 for possibly better answers to your question. – Justin Mar 02 '11 at 15:31