Stathread and Multithreading

STAThread and multithreading

Apartment threading is a COM concept; if you're not using COM, and none of the APIs you call use COM "under the covers", then you don't need to worry about apartments.

If you do need to be aware of apartments, then the details can get a little complicated; a probably-oversimplified version is that COM objects tagged as STA must be run on an STAThread, and COM objects marked MTA must be run on an MTA thread. Using these rules, COM can optimize calls between these different objects, avoiding marshaling where it isn't necessary.

How will STAThread affect my multi-threaded program

[STAThread] or Thread.SetApartmentState() are a really, really big deal. You make a promise to the operating system that you write code that is well-behaved. It matters to lots and lots of code inside Windows as well as components you use that are not thread-safe. Standard examples of such code are the Clipboard, Drag + Drop, the shell dialogs (like OpenFileDialog), components like WebBrowser and many Windows sub-components that are wrapped by .NET classes.

Thread-safety is always a big deal, writing truly thread-safe code is very, very difficult. The .NET Framework itself accomplishes it very rarely. Very basic classes list List<> are not thread-safe.

By making the promise to behave well, you must abide by the rules of writing code in a thread that reports itself to be an STA thread. You must do two basic things:

  1. You must pump a message loop. Aka Application.Run() in a Winforms or WPF app. A message loop is a basic mechanism by which you can get code to run on a specific thread. It is the universal solution to the producer-consumer problem. Which solves the thread-safety problem, if you call thread-unsafe code always from the same thread then it isn't unsafe anymore.

  2. You must never block your thread. Blocking an STA thread is very likely to cause deadlock. Because it stops those chunks of code that are not thread-safe from being called. There is core support for this in the CLR, blocking an STA thread with WaitOne() causes it to pump a message loop itself.

These requirements are easily met in a Winforms or WPF app. They are class libraries that were completely designed to help you implement them. Almost every single aspect about the way they behave was affected by it.

You must mark the Main() method in a GUI app as [STAThread]. Rock-hard requirement when it creates windows.

Creating another thread that displays a window is supported and possible. This time you must call SetApartmentState() to switch to STA, it cannot be a thread-pool thread. Getting this right is very difficult, in Winforms you'll get bitten badly by the SystemEvents class if you use certain kind of controls. It has a knack to start raising its events on the wrong thread. Debugging such a problem requires black-belt skills that look like this. That's suppose to scare you.

What is STA/MTA vs apartments/free threads vs UI threads/worker threads? Why the name changes?

It seems that the terms are interchangeable:























COM synchronizes callsCOM does not synchronize calls
STA (preferred name)MTA (preferred name)
"Apartment thread"Free thread
(often) UI thread(often) Worker thread

Is STAThread attribute a requirement or recommendation?

The [STAThread] attribute only works on the Main() entrypoint of a standalone executable program. It is a promise you make to the operating system, telling it that the main thread of your program is "well-behaved". It must pump a message loop and never block. Breaking the promise causes hard to diagnose misbehavior, deadlock is common.

None of this applies when you write an AutoCAD extension. You did not create the thread, AutoCAD did. You can't make any promises, it is AutoCAD that has to implement them. Nor is it a "main thread", that's a term that can only apply to a standalone program.

The thread that calls your extension is almost certainly already a single-threaded apartment, you can use Thread.GetApartmentState() in your code to double-check. STA is a requirement for a thread that displays UI, something that you very commonly do in an extension.

When creating a Thread on a class called from an STAThread instance, what will the apartment state be?

All threads default to MTAs, unless SetApartmentState is called before the thread is started.

If you think about it, the thread can't share the Single Threaded Apartment of an existing thread.

Starting an STAThread in C#

Thread thread = new Thread(() => MyClass.DoX("abc", "def"));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

If you need the value, you can "capture" that back into a variable, but note that the variable won't have the value until the end of the other thread:

int retVal = 0;
Thread thread = new Thread(() => {
retVal = MyClass.DoX("abc", "def");
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

or perhaps simpler:

Thread thread = new Thread(() => {
int retVal = MyClass.DoX("abc", "def");
// do something with retVal
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();


Related Topics



Leave a reply



Submit