COM multi-threading support

2020-07-13 11:46发布

问题:

Working with COM for the first time I have got this COM dll, say ABCServer.dll, i created a RCW and added reference to it in my project. Now my application creates several threads and each thread creates certain classes from COM dll and works with them. But then each thread is waiting while other thread is working with certain class from COM dll.

The whole purpose of modifying my application was enabling multi-threading on it. Now when multithreading is happening at my side, the COM causing it to be sequential. Although each thread is creating new instances, why are they waiting for others to be processed?

回答1:

There's considerable confusion in the comments and answers posted so far. STA or MTA is a property of a thread. But what matters is what the COM component requires. That's declared in the ThreadingModel registry value. You found "Apartment", a very common setting. It means that the component does not support threading.

This is not unlike the vast majority of classes in the .NET framework, there are very few that are thread-safe. The big difference is that COM enforces threading safety. With .NET classes it is up to you to write your code so the class objects are used in a thread-safe manner. That's difficult to get right and a chronic source of very hard to diagnose bugs but well designed locking allows you to sail around the restrictions. That's not possible with COM, it will always provide thread-safety without you helping. Even if you do want to help.

The only way to get ahead is to create and use the COM component from only one thread. That thread must be initialized with Thread.SetApartmentState() to select STA. Which prevents COM from creating its own thread to find a safe haven for the component. And you must pump a message loop, an STA requirement. Which can be painful and might be avoided if you don't try to use the COM component from another thread and the component itself doesn't depend on having a message pump available. Which is pretty common btw. You'll notice it needs one when it stops being responsive, typically not firing an event when expected. Only make calls on the COM object from the same thread to get concurrency with other threads that create their own object. This is not typically useful or possible.



回答2:

If your COM component is marked as STA (single-threaded apartment) then there's nothing you can do to make it multi-threaded; the requirement of the component is that all calls to it are serialized onto the thread that the STA is on, and COM handles that automatically for you.

That said, if your component is an STA component (and it seems like it is) and you can't change it to be a multi-threaded apartment component (MTA) or even better, free-threaded (so there is no marshaling between apartments at all) because a) it was written in VB6, or b) it's a third-party dll, then you might be better off working with some sort of queue model.

Basically, have all of your other work run asynchronously, then have a thread (or process, it's up to you), that will consume requests to call this component one at a time, as fast as it can (mind you, you can instantiate multiple instances of this component in multiple threads, you just have to make sure you set the ApartmentState property on the Thread class to ApartmentState.STA) and then publish events/callbacks when the call is complete and continue your other work asynchronously.

It's basically like having two producer/consumer implementations, one to dispatch the calls to the COM component, and another to dispatch the results when done.



回答3:

If each of your threads is creating its own instance of an Apartment-model object, and each thread is marked to run in an STA, then each thread should be in its own separate STA and your COM object instances should be running in separate STAs.

The other answers here assume, I think, that your COM instances are all in the same apartment. If you call Thread.SetApartmentState() to ensure each thread is in an STA before it creates the COM object instances, then that thread's COM objects should live in that thread's STA, which is separate from other threads' STAs. You should not then see any serialization of calls to objects in different STAs.