14

I've come across this problem several times while developing in C#. I'll be a happily coding along, passing objects to and fro between threads and what not, then all of a sudden I get this familiar error:

"The calling thread cannot access this object because a different thread owns it."

Well, ok, I've dealt with it before, especially with objects on the GUI thread. You just have to write some extra code to program around that specific problem. But every once in while I come across an object that is by all means ordinary, yet it doesn't like being accessed by different threads.

EDIT I was mistaken in my original post about the object that was causing the access exception. It was NOT IPAddress, instead its System.Printing.PrintQueue. which I was using to obtain the IP address. This is the object that you can't assess from more than 1 thread.

All my classes I've written never have this problem. I don't even know how I'd implement this myself. Would you have to keep a member variable with the thread ID that created you, and then check the current thread against that on every single property and method access? That seems crazy. Why would Microsoft decide that..... "OK... PrintQueue, definitely not sharable among threads. But these other classes.... their good to go."

Why are some objects blocked from multiple thread access?

Ultratrunks
  • 2,464
  • 5
  • 28
  • 48
  • 2
    Think about the alternative: any thread can access any object as it likes. Now that object has to either depend on the accessor to properly handle threading concerns, or it has to write a bunch of extra code to ensure that it's safe for accessors from multiple threads. Option 1 is not realistic, and option 2 is a lot of work. So the decision was made to go with option 3: disallow accessors from multiple threads. – dlev Jul 03 '12 at 19:07
  • 1
    Looks like the C# garbage disposal locks certain variables depending on how they are used. I've never had to deal with this but a good way to try and fix this problem is to put your variables into a static holder if you plan on sharing them among threads. One thread reads the other thread writes. You might need to do some locking to make sure you don't have collisions on write if both of them need to change the value. – Serguei Fedorov Jul 03 '12 at 19:11
  • 1
    Well, ALL my classes I've ever written in the last 10 years don't have this restriction. So am I'm doing it wrong then? Also, in this particular example. IPAddress is likely just a byte array under the hood so it can handle IPv4 and IPv6. So whats the big deal. And in my particular case, I don't want write access. I just want read access. So i have to pass a byte array instead, obfuscating my code for seemingly no apparent reason. – Ultratrunks Jul 03 '12 at 19:11
  • 1
    @dlev Your comment makes sense, but...hm. I am sure that there are *bunch* of classes in .NET Framework BCL that are not thread safe and that do allow access from multiple threads. When someone fails to implement threading properly while using these classes, he has to live with consequences. So what makes `IPAdress` so special? – Nikola Anusev Jul 03 '12 at 19:11
  • 3
    Also, I don't think you're correct about `IPAddress` displaying this behavior. I've never seen it, and can't reproduce it. Are you trying to display an IP address in a text box or something along those lines? – dlev Jul 03 '12 at 19:11
  • You could try looking with ILSPY at the sources to confirm your assumption that the IPAddress class is "just a byte array". – Uwe Keim Jul 03 '12 at 19:13
  • 1
    Note that WinForms UI objects display this behavior because they need to conform to the threading requirements of the underlying windows object. From the WPF architecture description: "In the end, WPF’s threading model was kept in sync with the existing User32 threading model of single threaded execution with thread affinity. The primary reason for this was interoperability – systems like OLE 2.0, the clipboard, and Internet Explorer all require single thread affinity (STA) execution." – dlev Jul 03 '12 at 19:16
  • @dlev Yes, I was mistaken about IPAddress. I was fetching the address by digging in System.Printing.PrintQueue which is the culprit here. I've edited my post to include the correction. But my question still stands, what makes this guy so special that Microsoft must prevent me from accessing it from more than 1 thread? – Ultratrunks Jul 03 '12 at 19:47

1 Answers1

3

I think this may explain things fairly well, I think this specifically has to do with COM.

http://msdn.microsoft.com/en-us/library/ms693344%28v=vs.85%29

specifically.

In general, the simplest way to view the COM threading architecture is to think of all the COM objects in the process as divided into groups called apartments. A COM object lives in exactly one apartment, in the sense that its methods can legally be directly called only by a thread that belongs to that apartment. Any other thread that wants to call the object must go through a proxy.

There are two types of apartments: single-threaded apartments, and multithreaded apartments.

Single-threaded apartments consist of exactly one thread, so all COM objects that live in a single-threaded apartment can receive method calls only from the one thread that belongs to that apartment. All method calls to a COM object in a single-threaded apartment are synchronized with the windows message queue for the single-threaded apartment's thread. A process with a single thread of execution is simply a special case of this model.

Multithreaded apartments consist of one or more threads, so all COM objects that live in an multithreaded apartment can receive method calls directly from any of the threads that belong to the multithreaded apartment. Threads in a multithreaded apartment use a model called free-threading. Calls to COM objects in a multithreaded apartment are synchronized by the objects themselves.

Matthew Vines
  • 27,253
  • 7
  • 76
  • 97
  • 2
    So are you implying that the System.Printing.PrintQueue object is really a STA COM object, and thats why it can't be touched by other threads? From looking at System.Printing.PrintQueue documentation, how can I tell that this is a STA COM object? – Ultratrunks Jul 03 '12 at 19:32
  • Interesting stuff. I think this http://msdn.microsoft.com/en-us/library/5s8ee185.aspx complements the above answer (and also explains why Ultratrunks encountered such problems in conjunction with GUI programming). – jpe Jul 03 '12 at 19:54
  • @Ultratrunks I'm fairly certain that class is COM wrapper since it accesses the print queue. However, I do not know how to know up front what is STA. There is a thread safety section for classes in MSDN, but I didn't see what I expected to see in there. – Matthew Vines Jul 03 '12 at 20:14