Reputation: 821
I've recently encountered a STA-related error in my program when I tried to launch an OpenFileDialog
in a WinForm. I've done some reading, and before I add the [STAThread]
attribute to my main thread I want to know how it will affect my program's execution.
I am a foreigner to COM so not everything I read made sense to me. Some points that stuck with me are:
The [STAThread] attribute defines the application as using a single-threaded apartment model. More specifically, it changes the state of the application thread to be single-threaded. http://www.a2zdotnet.com/View.aspx?Id=93
The STA architecture can impose significant performance penalties when an object is accessed by many threads. Each thread's access to the object is serialized and so each thread must wait in line for its turn to have a go with the object.
http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa
I understand the need for thread-safety but I still don't understand what STAThread does. In my program (which I inherited from another developer) the main thread launches several other threads, one of which initializes the UI forms - and I think this is where the problem arises. With [STAThread]
added what happens to the new threads? Does this affect multi-thread communication for non-Windows objects?
The error occurs when I try to open an OpenFileDialog
in one of my forms. I added the dialog to the form using the VS designer: it didn't work. I then attempted to create a dialog box in a global file which is run by the main thread and call that instance from my form. It had no effect.
Upvotes: 3
Views: 3582
Reputation: 942109
[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:
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.
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.
Upvotes: 8