Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining STA: "single thread affinity" or "single threaded apartment"; and how they relate

I'm working with WPF and am reading through materials on STA. Two questions:

1) STA is defined as standing for both "single thread affinity" and "single threaded apartment" by different articles. This says the former: https://msdn.microsoft.com/en-us/library/ms750441(v=vs.110).aspx This says the latter: https://msdn.microsoft.com/en-us/library/ms742522(v=vs.110).aspx

Which is it, or can STA refer to either of these related concepts?

2) Is it correct to describe their relationship as: Single threaded apartment is a model used by various components of Windows, thread affinity is a characteristic of that model?

Thanks.

like image 730
user603563 Avatar asked Dec 02 '22 17:12

user603563


1 Answers

STA means "Single Threaded Apartment". It is a COM concept, highly unrelated to WPF. The WPF designers learned from the mistakes in .NET 1.x, many programmers wrote programs that violated thread-safety requirements and had a very difficult time fixing their programs. So they added more runtime checks in .NET framework code to help them keep out of trouble.

While it is pure COM concept, the underlying principle is pretty universal. By declaring a thread STA, you make a promise that the code you write is well-behaved. You write well-behaved code by running a dispatcher loop (Application.Run) and never block code that runs on the thread. Breaking this promise causes deadlock.

The vast majority of .NET classes, as well as most of the code you write yourself, is thread-unsafe. There are two basic ways to make such code thread-safe. The first one is the approach you take with a class like List<>, you put lock statements in your code to ensure that the List object can only be accessed by a single thread at a time.

That usually works fine, but the more elaborate the class gets, the harder it gets to figure out where to put the lock statements and the higher the odds that such locks cause deadlock. The only other option you then have to keep the code thread-safe is to only ever make method calls on the same thread. All of the WPF components are like that, you must call Dispatcher.Begin/Invoke() from a worker thread to set their properties or call their methods. You can now say that the object has thread-affinity, it is only usable from the thread that created it.

The WPF designers wanted to add those runtime checks to tell the programmer he's using the WPF component wrong. The component can only work on a thread that called Application.Run() so that Dispatcher.Begin/Invoke() is going to work. Problem is, they can't tell whether the thread is going to call Application.Run(). That often happens later. A promise is needed.

So they borrowed the COM promise, the apartment state of a thread is always set and gives a decent hint how it is going to behave. No guarantee, just a decent hint. The main thread of a WPF app is almost always appropriate, it is STA thanks to the [STAThread] attribute on the Main() entrypoint and the project template ensures it calls Application.Run(). And a Task or thread-pool thread is in the MTA and will never call Application.Run() so is a very hostile place for a WPF component.

The exception they generate keeps the programmer out of trouble. Note how you can very easily suppress this exception. All you have to do is call Thread.SetApartmentState() to make the promise. But of course you are now flying without the safety net, WPF can no longer tell you that you are doing it wrong, it is now entirely up to you to write the code correctly. You must call Application.Run(), even though you most typically do not want to do that.

like image 83
Hans Passant Avatar answered Dec 21 '22 03:12

Hans Passant