Access to a COM object from a thread, except for the main thread Slow In C #

I have my own COM library that returns an array of integers (in its own native format, of course). When I access this array from the main UI thread, everything is fine and works fast. When I access it from another thread, access is very slow. Here is the 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 to speed this up? If I use multiprocessing instead of multithreading, then will I have any hope of getting good performance? (Wow, though, sounds a lot more complicated.)

UPDATE:

I am satisfied with the answers below, but wanted to add some data here for reference (my own and others).

Creating and accessing an object in a new thread, as shown above, gives about 12 ns access. Presumably, the object is actually created in the main thread, and slow speed is associated with data marshaling from there.

If you explicitly create data in the main stream, but get access to it in a new stream marked as a single-threaded apartment, the access time is even slower - 15 ns per access. I believe that .NET should have extra overhead to keep the apartment in good condition, although it bothers me that I do not know what this overhead is. With a difference of 2-3 ns this should not be much.

If you create and access an object in a new thread marked by STA, the time will melt in .2ns per access. But is this new thread really safe? This is a question for another question that I think.

+4
source share
3 answers

COM objects are similar to threads; they can tell COM that they are not thread safe. With the key in the registry key is "ThreadingModel". The vast majority do this by specifying "Apartment" or simply by missing the key. In .NET, it is less explicit, it uses MSDN to tell you that the classes are not thread safe and do not actually remind you that you forgot to read the article. The vast majority of .NET classes are not thread safe, no different from COM COM classes. Unlike .NET, COM ensures that they are invoked in a thread-safe manner. Automatically marching a call to the thread that created the object.

In other words, no concurrency and very slow.

The only way to succeed is to create your own thread and call its SetApartmentState () method to switch to STA, a happy home for a COM object that is not thread safe. And you must also create a COM object in this thread. And you may have to pump the outline of the message to save it, an STA requirement. And never block the thread. This is what makes it a happy home for a class that is not thread safe, if all calls are made on the same thread, nothing can go wrong. You can find an example of implementing such a stream in this answer .

Or, in other words, there is no free lunch when using streams with objects that are not thread safe .. NET allows you to remove your foot, forgetting to use the lock where necessary, COM makes it automatic. Far fewer programmers jump on one leg this way, but not as effective.

+8
source

This may depend on the model of the multi-threaded apartment. If you are using a single-threaded apartment (STA) model, you may run into performance issues (if the data size is large enough). If you can (then if you are not using another COM object that needs an STA), you can try changing the apartment model to MTA (multi-threaded apartment model).

Note. WinForms is not compatible with MTA, it always checks that the apartment model is single, because it is required for some of the COM objects that it uses (for example, Clipboard and Drag and Drop). I have never tried, but if you are not using these features, perhaps this works.

From MSDN :

Because object calls are not serialized in any way, the multi-threaded concurrency object provides the highest performance and makes the best use of multi-processor equipment for end-to-end flows, cross-process and cross-call.

Sitelinks
Here on SO: Could you explain STA and MTA?
MSDN: MTAThreadAttribute

+2
source

Try making a COM call in the user interface thread using Invoke() :

 private void button1_Click(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(delegate { this.Invoke((Action)(() => { test(); })); }); } 

Do the rest of your lengthy work before and after this call before Invoke() so that only a quick COM call is made to the user interface thread. Also, depending on what you are doing, you can probably get rid of a lot of brackets and other linear noise.

+1
source

Source: https://habr.com/ru/post/1413062/


All Articles