Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread Apartment State

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.

like image 696
koumides Avatar asked Dec 22 '22 17:12

koumides


1 Answers

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.

like image 52
Hans Passant Avatar answered Jan 08 '23 02:01

Hans Passant