Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing COM Object From Thread Other Than Main Thread Slow In C#

I have a proprietary COM library that returns an array of integers (in their own proprietary format of course). When I access this array from the main UI thread, all is well and runs quickly. When I access it from another thread, the access is very slow. There's some sample code below.

private void test() {
    ProprietaryLib.Integers ints = ProprietaryLib.GetInts();
    int x;
    for(int i = 0; i < 500; i++)
        for(int j = 0; j < ints.Count; j++)
            x = ints[j];
}

private void button1_Click(object sender, EventArgs e) {
    test();  // Very little time
    new System.Threading.Thread(() => test()).Start(); // Lots of time
}

Why is this happening? Is there any way for me to speed this up? If I use multi-processing instead of multi-threading, would I then have some hope of getting good performance? (Ugh though, sounds a lot more complicated.)

UPDATE:

I'm satisfied with the answers below, but wanted to add some data here for reference (my own and anyone else's).

Creating and accessing object in a new thread as shown above gives about 12ns per access. Presumably the object is actually created on the main thread and the slow speed is due to marshaling the data from there.

If you explicitly create the data on the main thread but access it in a new thread marked as a single threaded apartment, access time is even slower, at 15 ns per access. I guess .NET must have some extra overhead to keep the apartment nice, though it worries me that I don't know what that overhead is. With just a 2-3 ns difference it wouldn't have to be much though.

If you create and access the object on a new thread marked STA the time melts away at .2ns per access. But is this new thread really safe? That's a question for another question I think.

like image 322
user12861 Avatar asked Dec 20 '22 21:12

user12861


1 Answers

COM objects have threading affinity, they can tell COM that they are not thread-safe. With a key in the registry, the "ThreadingModel" key. The vast majority do, either by specifying "Apartment" or just omitting key. It is less explicit in .NET, it uses MSDN to tell you that classes are not thread-safe and doesn't otherwise remind you that you forgot to read the article. The vast majority of .NET classes are not thread-safe, no different from COM coclasses. Unlike .NET, COM makes sure that they get called in a thread-safe way. By automatically marshaling the call to the thread that created the object.

In other words, no concurrency and very slow.

The only way to get ahead is to create your own Thread and call its SetApartmentState() method to switch to STA, a happy home for a COM object that isn't thread-safe. And you have to create the COM object on that thread as well. And you may have to pump a message loop to keep it alive, an STA requirement. And never block the thread. These are the things that make it a happy home for a class that is not thread-safe, if all the calls are made on one thread then nothing can go wrong. You can find a sample implementation of such a thread in this answer.

Or in other words, there is no free lunch when using threads with objects that are not thread-safe. .NET lets you shoot your foot by forgetting to use lock where needed, COM makes it automatic. A lot less programmers hopping on one leg that way, but not as efficient.

like image 92
Hans Passant Avatar answered Dec 23 '22 10:12

Hans Passant