Reputation: 33
This question is related to the top rated comment of this question:
The STA model is used for COM objects that are not thread safe. That means they do not handle their own synchronization. A common use of this is a UI component. So if another thread needs to interact with the object (such as pushing a button in a form) then the message is marshalled onto the STA thread. The windows forms message pumping system is an example of this.
So the following example confuses me a bit..
Main is annotated with [STAThread]
.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
I was supposing, that, since Form1 runs in a STAThread, every message to a UI-Component that is sent from another thread is marshalled onto the STA thread.
Therefore I expected, that a call like this would work:
public Form1()
{
InitializeComponent();
Thread t = new Thread(() => { this.label1.Text = "test"; });
t.IsBackground = true;
t.Start();
}
As discussed in many other posts, it won't work. Thread t ...
has to be replaced with something like Thread t = new Thread(()=> { this.label1.Invoke((Action)(() => { this.label1.Text = "test"; })); });
to fix it, but having in mind, that the STA should marshall this.label1.Text = "test";
to the mainthread, why do I still get the invalid cross-thread access error?
Edit Solved:
Accepted Answer:
[STAThread]
(and[MTAThread]
) are only related to working with COM interop.
Since the Label-Object seems to be just a wrapper for a Win32 control, it is no COM-Object (as wrongly expected from me due to the blockquote at the beginning of the question) and so calls will not be marshalled. There are checks for calls from other threads inside the wrapper, but marshalling via (Begin)Invoke has to be done by the developer.
The coming up question is of course: how could the wrapper be modified, so this is done automatically and downwardly compatible.. but that's another topic, I guess.
Upvotes: 2
Views: 577
Reputation: 148160
STA stands for single threaded apartments used to make sure that one thread can access COM component at a time. The exception you are getting is not because of synchronization of COM with multiple threads but access the control in a thread on which it is not created.
The label control is created in GUI thread and should be accessed on thread its being created i.e GUI thread. You are trying to access it on non GUI thread that is why you are getting cross thread exception. The Invoke method will access the Label on GUI thread and you wont get cross thread exception.
Comment of OP
in this example - the STA is marshalling each call to a UI-Component from another thread onto the UI-Thread, so it is actually executed on the UI-Thread and therefore is no cross threading anymore. Or is marshalling working in a different way?
Marshaling is the mechanism by which an object that is accessible to one apartment can be made accessible to another apartment, reference. On COM components when you invoke method the call is executed originating apartment and results are returned to originator of call. On the other hand for non COM components the STA apartment won’t come in action. The restriction of accessing the GUI objects on thread they are being created that is imposed by .net for non COM components like Label, Button etc. has to be handled by using methods like Invoke for instance when being accessed by thread other than GUI thread.
Marshaling Since the rules for apartment model objects is that they can be accessed only from the thread on which they were created, you need to do some extra work if you want to access them from another thread: You need to hire a lackey. COM calls this lackey a "proxy". When you invoke a method on the proxy object, the call is routed back to the originating apartment, the method executes on its original apartment, and then the results are routed back to original caller. (And if any of the parameters to the method are themselves objects, then COM needs to create proxies for those objects, too!) Marshaling is a mechanism for creating proxies.
COM threading models only apply to applications that use COM interop. The COM threading model can be set to single-threaded apartment or multithreaded apartment. The application thread is only initialized for COM interop if the thread actually makes a call to a COM component. If COM interop is not used, then the thread is not initialized, and the STAThreadAttribute attribute, if it is present, has no effect.
How to: Make Thread-Safe Calls to Windows Forms Controls
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way. It is unsafe to call a control from a thread other than the one that created the control without using the Invoke method. The following is an example of a call that is not thread safe.
Upvotes: 3
Reputation: 9595
By default .net objects does not have any kind of multi-thread marshaling.
[STAThread]
(and [MTAThread]
) are only related to working with COM interop. Generally these attributes instructs how to initialize thread via CoInitializeEx call. So real marshalling will happen only when accessing COM objects and it will be done by COM subsystem, not .net.
UI is a different beast. Actually most of Win32 controls that used under the hood are thread safe (because they are based on message paradigm), but .net wrappers around them are not. So, to prevent inconsistent state while working with UI, all WinForms classes have special checks for accessing them from different threads.
To summarize, when accessing WinForms classes from different thread no default marshalling happen, there is only simple thread check, and that is why you should do marshaling by yourself.
Upvotes: 1