views:

601

answers:

7

Given: Constructing an ADO Connection object from one thread and giving it to another thread is forbidden. The two threads are different apartments, and even though the first thread will never touch it again (not even maintain a reference to it!), it doesn't matter.

That ADO Connection object was created by ThreadA, ThreadA is the only thread, ever, under any circumstances, at all, ever, that is allowed to use that Connection object, ever.

Now replace "ADO Connection" with "ADO.NET Connection". Does the same rule apply?


i know that most objects in the .NET framework are not thread-safe. For example, the DictionaryEntry structure in the SDK says:

Thread Safety
Any public static members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

i understand that not thread-safe means that i have to synchronize access to the object if i am going to access it from different threads. That's all well and good, and i could ensure only one thread access the object at a time:

lock (myObject)
{
   ...
}

But there's something more than not being thread-safe.

In COM, (some) objects are bound to the "apartment" that created it. Once the object has been constructed on one apartment, you are forbidden from accessing it from another apartment - no matter how much you protect that object from multiple simultaneous thread access.

Does a similar concept exist in .NET?


More Information

i know you are forbidden from accessing Controls from threads other than the one that created it - even if you use it in a thread-safe manner. This is not documented on MSDN:

Thread Safety

Only the following members are thread safe: BeginInvoke, EndInvoke, Invoke, InvokeRequired, and CreateGraphics if the handle for the control has already been created. Calling CreateGraphics before the control's handle has been created on a background thread can cause illegal cross thread calls.

There is no mention of Controls throwing exceptions when you create and use them from a single thread - when that thread is not the first thread that was created when the application started.

But what about arbitrary objects? What about:

public class MyClass
{
    int _number;

    public int Number { get { return _number; } set { _number = value; } }
}

MyClass myObject = new MyClass();

As long as i synchronize access to myObject two threads are allowed to talk to it?


The same goes for:

List<Object> sharedList = new List<Object>();

Two threads can talk to the list, as long as they don't do it simultaneously, usually with:

lock (sharedList)
{
    sharedList.Add(data);
}

are two threads allowed to touch the same object?


The same goes for:

IAsyncResult ar = BeginSetLabelToTheValueINeed(label1);
...
EndSetLabelToTheValueINeed(ar);


The same goes for:

//Fetch image on connection that is an existing DB transaction
public static Bitmap GetImageThumbnail(DbConnection conn, int imageID) 
{
}

being converted into the asynchronous delegate pattern:

//Begin fetching an image on connection that is an existing DB transaction
IAsyncResult ar = BeginGetImageThumbnuts(conn, imageID, callback, stateOjbect);
...
//Finish fetching an image on connection that is an existing DB transaction
Bitmap thumb = EndGetImageNumbthail(ar);


Rather than answering the question, people went off on a discussion about design patterns in ADO.NET. Please answer the question. Ignore the examples if they confuse and distract your squirrel brains.

A: 

Why would you want to create a connection on one thread and use it from another?

The time between connection creation/open and close/dispose should be minimal as is possible, so handing it off to another thread does not sound like a good idea regardless of whether or not its possible.

StingyJack
If you dont like this, then bite me.
StingyJack
A: 

First off, you're not required to synchronize your access to objects in multi-threaded scenarios. You can freely "fiddle" with any members of your own classes from multiple threads without any restrictions from the CLR. The fact the the Form (and Controls in general) don't allow you to call members on another thread is an implementation detail (and one that wasn't explicit before .Net 2.0).

All that being said, you'll usually need to enforce some type of synchronization to doing anything useful with objects accessibly by multiple threads.

On to your specific question. I believe that the SqlConnection can be used from multiple threads, but not at the same time. Not being able to concurrently use the SqlConnection almost complete negates any performance benefit you may get by using a shared connection. Given how cheap it is to create a new SqlConnection (especially once the connection is pooled), it makes very little sense to reuse the connection like this.

akmad
A: 

There are only a few use cases I know for desiring the above:

  • Working with SqlCe as it's a performance trick
  • Working with NHibernate and keeping the connection alive the whole time (although this basically is against NHibernate best practice)

Aside from those two (plus maybe a few others?) you don't really want to do what you're asking here.

I agree with the StingyJack, in most cases your ADO objects should appear within usings (created and destroyed within scope) and be local variables thereby eliminating thread concerns.

The deal with WinForm is to do with the difference between POST and SEND messages (send messages block to the consumer until they complete). In reality it doesn't throw an exception, the exception is just the warning added in 2.00 in debug. In practice, without the exception it results in (seemingly) random lock ups.

Quibblesome
A: 

See this handy page from Alik Levin

http://blogs.msdn.com/alikl/archive/2008/06/20/pitfalls-with-net-multithreading-and-com-objects-threads-must-have-compatible-apartment-models-mta-vs-sta.aspx

.net Object are MTA modelled. and this is only a concearn if you are interacting with COM objects

Pitfalls With .Net Multithreading And COM Objects – Threads Must Have Compatible Apartment Models (MTA vs. STA)

Be alert when implementing multithreading .Net in conjunction with COM objects. Thread apartment models matter.

.Net threads have Multi Threaded Apartment (MTA) model by default. COM objects have Single Thread Apartment (STA). Calling on COM objects on .Net threads that you spawn may cause unpredicted result.

Multithreading in .Net is easily implemented based on either Thread or ThreadPool objects. Thread.Start() method spawns new thread which has Multi Threaded Apartment (MTA) model . ThreadPool.QueueUserWorkItem(myMethod) queues myMethod to be executed on available thread managed by it.

+1  A: 

Objects in .NET are not apartment-bound unless you specifically make them so.

For instance, if you use thread local storage, you're going to have problems with multiple threads accessing the object, as long as you assume all access to the object will happen on one thread.

On the other hand, thread local storage could be a feature, where usage of the object across multiple threads are handled at least partially separate.

As for your other cases, let's take them one at a time, with comments:


But what about arbitrary objects? What about:

public class MyClass
{
    int _number;

    public int Number { get { return _number; } set { _number = value; } }
}

MyClass myObject = new MyClass();

As long as i synchronize access to myObject two threads are allowed to talk to it?

Answer: Yes, and in this case the need for synchronization depends on your requirements.

If you timing-wise absolutely have to guarantee that if on one thread set a new value into the object, and on another read it just after setting it (but two different threads), you require the second thread to read the value the first one put in, then you need locking.

However, storing the value for an int is an atomic operation. The lock is needed to get a memory barrier so that reading the value on the second thread won't use a cached copy.

You can usually get by on such simple classes with just declaring things volatile, but locking is a sure way to make better guarantees.

For a more complex object, say setting a struct bigger than the native bus-size of the cpu (ie. bigger than 32 or 64-bit, depending), a lock is required, since copying the data into the right spot in memory isn't an atomic operation in such a case. In other words, without the lock you risk reading half old half new data if one thread tries to read the data in the middle of a write operation executing on another thread.


The same goes for:

List<Object> sharedList = new List<Object>();

Two threads can talk to the list, as long as they don't do it simultaneously, usually with:

lock (sharedList)
{
    sharedList.Add(data);
}

are two threads allowed to touch the same object?

Answer: Yes. It's usually a good idea to use the SyncRoot property of the ICollection interface on collections, or just use a different locking object altogether.


The same goes for:

IAsyncResult ar = BeginSetLabelToTheValueINeed(label1);
...
EndSetLabelToTheValueINeed(ar);

Answer: This one I'm unsure about, which one is the problematic variable here? If label1 then there is nothing here that prevents multiple threads accessing and messing with this variable, and you're required to use locking mechanisms to prevent such problems.


The same goes for:

//Fetch image on connection that is an existing DB transaction
public static Bitmap GetImageThumbnail(DbConnection conn, int imageID) 
{
}

being converted into the asynchronous delegate pattern:

//Begin fetching an image on connection that is an existing DB transaction
IAsyncResult ar = BeginGetImageThumbnuts(conn, imageID, callback, stateOjbect);
...
//Finish fetching an image on connection that is an existing DB transaction
Bitmap thumb = EndGetImageNumbthail(ar);

Answer: If by this you mean you will execute multiple such requests in parallel, you're likely to not gain much, as you will need to serialize access to the connection object, since these aren't thread safe. You're probably better off by making the thumbnail method open and close its own connection.

Unless, of course, you want to only use one connection and instead serialize that access but parallelize the calculation of the thumbnail, but since these threads will in some way execute in sequence, due to most of them waiting for the first to finish accessing the connection, you're likely to not gain much here.


In short, locking is usually very easy to implement and can almost always provide good guarantees that you cannot mess up the object by accessing it from two threads.

Lasse V. Karlsen
You cannot talk to a control from multiple threads - at all - no matter how much synchronizing you do. Even if you only touch it from a background thread, and never from the GUI thread, that's not allowed.
Ian Boyd
It depends on the control. If it's a built in framework control, sure, but one you're making yourself, then yes, you can do a lot of damage here.
Lasse V. Karlsen
+3  A: 

.NET objects are not bound to an apartment. Apartments only really come into play when your .NET code interacts with COM objects, though this is not hard to do unknowingly. For example, if you interact with the Windows clipboard, you are activating the COM subsystem. Apartments are set at the thread level, and once your thread has established its apartment (MTA, STA, etc.) it cannot change, so it is very important to get the apartment correct. All WinForms applications have [STAThread] attached to their Main() method to ensure the main application thread (i.e. the GUI message pumping thread) is STA and can interact with those convenient facilities like the Windows clipboard.

The .NET runtime must be aware of the COM subsystem to provide COM interop functionality. It is pretty rare though to need the inner gruesome details.

From what I understand, one of the goals of dividing COM objects into different apartments was so that applications could use software components developed with different threading models without having to be concerned with such issues themselves.

There are a lot of complex issues involved here, particularly the notion that single threaded libraries are often not built to deal with re-entrancy and certainly do not support the notion of having two threads of execution modifying the object simultaneously.

For example, suppose you want to use a COM object in your new multi-threaded C++ application. This COM object was developed in Visual Basic and is not thread safe, but you want to use multiple instances of that component from multiple threads. While it is true that you could add some locking mechanism to prevent improper access, the fact that the object exists in its own Single Thread Apartment (STA) means that the COM subsystem will serialize access to the object on your behalf. You don't have to know the threading model of the COM object to use it, or at least that is the way it usually works.

.NET was written for our new multi-threaded world, and as such, does not use the concepts of apartments. The runtime is definitely aware of them, and has to be, to support interop with legacy COM objects.

Apartments are very complex and lots of weird things can go wrong if you do not know what you are doing. Be glad you do not generally need to worry about it in .NET.

If you want some really hard core information, you might check out Apartments and Pumping in the CLR.

Jeremy
thanks for pointing out my errors. I've updated my answer; unfortunately you can't take the bounty away once given. I'll keep you in mind when I'm reading answers, tho *wink wink*.
Will
That's not entirely true. You are not allowed to touch the properties of visual controls from a thread other than the one that created it. That control is 'bound' to the thread that created it. It's not a matter of synchronized access: it's just forbidden.
Ian Boyd
True, but I understood your question in a more general sense. Yes, some OS level resources, or libraries that are used by .NET objects, are bound to a thread.
Jeremy
+2  A: 

SHOOTING for the bounty!

Okay, some classes/frameworks of classes in .NET have methods that are apartment bound, but BY DESIGN ONLY. That means you have to SPECIFICALLY CODE to do this. Its not set by default. Coding for this is kinda kludgy. You have to get the thread ID you want to stick with and check it all the time. I don't think there are any facilities in the framework that makes this easy, other than those for UI components tracking the UI thread. That support framework is probably buried deep within the UI stack; I've never seen it in any MSDN docs.

What IS more common is setting the thread apartment. Lots of parts of the entire framework are restricted to STA threads by design (STAThreadAttribute). This is usually because they, deep down in their core, touch COM objects which often require that they are only used in STA threads. For example, the main thread in winforms apps is marked with the STAThreadAttribute, so that every UI component can only be touched within the same thread they are created, the UI thread. Interestingly, the XamlReader in WPF is aware of the current thread apartment and won't even deserialize UI components unless the thread its running in is an STA thread.

"Does a standard POCO have thread affinity or care if its in a STA or MTA thread?" The answer is no, unless you code it to be so or mark its methods with the STAThreadAttribute or if the object is instantiated in a call stack that, somewhere in its hierarchy, has a method marked with the STAThreadAttribute.

"Does an ADO.NET object have thread affinity, and is it apartment aware?"

Well, a DbConnection extends from Component, which extends MarshalByRefObject. Component, MBRO, DbConnection and its descendents (i.e., SqlConnection) contain no methods that are marked with the STAThreadAttribute. A quick check of their major dependencies (such as DbTransaction) doesn't find that any of THOSE classes' methods are marked as STA only as well. Therefore they are not apartment aware.

A quick browse with Reflector doesn't find any obvious thread-id watching. DbConnection does depend on Transaction, which seems like the most likely thread-fondler of the bunch.

I busted out Reflector and checked out a few of these classes.
DbConnection - Doesn't care about the current thread's ID
SqlConnection - Does some thread tracking when debugging
SqlTransaction - Has a function called "ZombieCheck", but no thread watching
DbTransaction - Nothing here.

So I think its safe to say that ADO.NET objects do NOT have thread affinity. I didn't even see them use Thread.Begin/EndThreadAffinity.


EDIT

As pointed out in the comments by Jeremy, I kept talking about the CLASSES being marked with the attribute rather than the CLASSES' METHODS. HUUUGE mistake. I screwed up bigtime by giving the impression that I only looked at the class definitions rather than the entire class.

I've edited this to clarify, and let me say it here to be absolutely clear: No class methods in System.Data are marked with the STAThreadAttribute. You can check this for yourself with the following hacky code:

SqlConnection sc = new SqlConnection();
var data = AppDomain.CurrentDomain.GetAssemblies()
           .Where(x => x.FullName.StartsWith("System.Data,")).First();
var dtypes = data.GetTypes();
var results = new List<string>();
foreach (var type in dtypes)
    foreach (var method in type.GetMethods())
        foreach (var attr in 
                method.GetCustomAttributes(true).OfType<System.Attribute>())
        {
            results.Add(string.Format("{0} {1} {2}", 
                               type.Name, method.Name, attr.GetType().Name));
            if (attr.GetType() == typeof(STAThreadAttribute)) 
                                           throw new Exception("SHIII");
        }

Also, UI classes in winforms also don't use the STAThreadAttribute; its the application where this attribute is used (as, again, Jeremy pointed out).

There is a special case where I've run into a class being apartment aware. The XamlReader, which deserializes xaml and creates WPF UI classes, will throw an exception when its Load method is called on a MTA thread. Its not even in the docs, which is annoying... So, again, classes aren't apartment aware by default; you have to code it that way or you have to make their instances aware by newing them up in a method chain containing a method marked by the STAThreadAttribute. Whew. This stuff is harder to explain than understand...

Will
You win because you specifically pointed out the link between [STAThread] and objects being on an apartment. This seems to indicate that the only way objects are apartment locked is the STAThread. So objects are not apartment bound without the [..apartment model...] tag. Very nice.
Ian Boyd
Thanks. I'll use your 300 bounty points to pay out a couple myself, hopefully!
Will
One small correction, you do not mark an object with [STAThread], you mark a method, and particularly you mark your program entry point (Main). Once you set an apartment for a thread, you cannot change it. Putting [STAThread] on a random object has no effect.
Jeremy
Then i guess i need to recind the bounty answer. If [STAThread] is used to set the apps threading model, the original question still stands: are objects apartment bound?
Ian Boyd
The bounty does not really matter, I updated my answer to reflect how (I believe) things work. COM is hard! :)
Jeremy
Ugh, I screwed that one up. I've clarified and added some stuff to make up for it. Obviously, the bounty system needs to allow taking the bounty back within X days for cases like this. Upvoted Jeremy at least.
Will
:) Like I said, does not matter. Your answer is very informative, just that my programmer over-exactness could not let it go!
Jeremy
My programmer over-exactness is beating my head in over the fact that I didn't catch it in the three times I re-read it before, during and after posting it.
Will
So after all this i still don't understand. i construct a ListView object is one thread, trying to touch it from another thread throws an exception. Is the ListView not 'bound' to the thread that created it?
Ian Boyd
The underlying Windows controls are bound to the thread, but not the C# object that represents it. Or more generic, sometimes a C# object controls an underlying resource that is bound to a thread. If so, I guess you could say it "inherits" that property. But random C# objects are not bound.
Jeremy
Again, apartment binding is NOT automatic. You have to CODE to do this. UI classes are CODED to do this. All other classes don't care.
Will