GOTCHA 67 Cross-apartment calls are expensive


GOTCHA #67 Cross-apartment calls are expensive

One of the goals of COM is to make it easier to substitute one component for another. You should be able to swap out a component that supports a set of interfaces and replace it with another one that supports the same set of interfaces and abides by their implied contract. The client must be able to interchange multiple components that satisfy a set of interface contracts, and interact with all of them in the same way.

What is the purpose of an apartment [Box98]? It is to make the component substitutable while having different threading needs. Say a component A takes care of thread safety. It is meant to be invoked from multiple threads simultaneously. Say another component B, which fulfills the same interface contract as component A, doesn't deal with thread safety. It is meant to be invoked from a single thread. How can a client swap these two components seamlessly?

The purpose of an apartment is to provide a logical thread isolation boundary in COM. A client thread executes in an apartment. The object may be created in the same or a different apartment. If the object is in the same apartment as the client, the calls to its methods are executed directly by the client thread. If they are in different apartments, then the client communicates using a proxy, and a method call request is sent to the other apartment. One of the threads in the other apartment picks up the request and executes it. An object that is not thread-safe is created in a Single Threaded Apartment (STA) while an object that can handle multiple threads is created in a Multithreaded Apartment (MTA). Once the apartment in which a thread will execute is decided, it is pretty much set.

While multiple threads may execute in an MTA, only one thread ever executes in an STA. What is the effect of multiple threads invoking a method on an object at the same time? If the object is in an STA, then the method calls are queued and the single thread that resides in the object's STA picks one call at a time and executes it. This guarantees that only one thread calls the object's methods at any time. If the object, however, is in an MTA, then multiple threads in the MTA will pick up requests from the queue and execute them simultaneously. Of course, if any of the calling threads are in the same apartment as the object, then the call will be direct and not go through a proxy, stub, and call message queue.

So, now that I have reviewed what apartments in COM are, what is the significance of this when it comes to .NET code that interacts with a COM component? If the component and client are in the same apartment, you have no COM overhead. You incur only the marshalling overhead (to go from .NET through an RCW to COM). However, if the client and the component are in different apartments, then you incur overhead at both levels. This is shown in Figure 8-5 and demonstrated in Example 8-3.

Figure 8-5. Overhead associated with cross-apartment invocation


Example 8-3. Effect of apartment-related attributes

C# (Apartment)

 using System; namespace COMCompUser {     class Test     {         [STAThread] // You will see the effect of this attribute         static void Main(string[] args)         {             MyCOMCompLib.IMyComp theMyComp                 = new MyCOMCompLib.MyCompClass();             theMyComp.Method1();         }     } } 

VB.NET (Apartment)

 Module Test     'You will see the effect of this attribute     <STAThread()> _     Sub Main()         Dim theMyComp as MyCOMCompLib.IMyComp _                 = new MyCOMCompLib.MyCompClass()         theMyComp.Method1()     End Sub End Module 

In this example, you create a COM component (actually the RCW of the COM component) and call Method1() on it. You place the STAThread attribute on the Main() method. Now, let's put a breakpoint in the COM component's Method1() and look at the call stack. The stack trace is shown in Figure 8-6.

Figure 8-6. Stack trace of Method1 of COM component in Example 8-3


You are only two levels deep in the call stack in this case. Now, change the STAThread attribute in the Main() method to MTAThread, as shown in Example 8-4, and rerun the program.

Example 8-4. Changing the apartment of Main() method

C# (Apartment)

         //...         [MTAThread] // You will see the effect of this attribute         static void Main(string[] args)         //... 

VB.NET (Apartment)

     '...     'You will see the effect of this attribute     <MTAThread()> _     Sub Main()         '... 

Figure 8-7 shows the new call stack (or as much of it as will fit).

The call stack is 59 levels deep. What made the difference? The client code is running in an MTA while the component resides in a STA. The call to the method now has to go through a proxy and a stub. It is better to avoid this overhead when invoking COM components. While in principle COM isolates the threading needs of a component from its client, you still need to be sensitive to the impact on performance.

So how do you interact efficiently with two components that have different threading models? You might consider creating two different threads in your .NET applica

Figure 8-7. COM component's Method1() call stack for Example 8-4


tion, one in an STA and the other in an MTA, then interact with the COM components from the appropriate threads, based on the COM component's apartment. This is another reason to isolate the interaction with the component as discussed in Gotcha #70, "Spattering access to COM components makes code hard to maintain." You can find the apartment of the COM object by looking at the ThreadingModel in its Registry settings, or by reading its documentation. Details of setting the apartment of a thread are presented later in Example 8-7.

IN A NUTSHELL

Understand the apartment of your thread to get the best performance when interacting with COM components. If possible, invoke methods on a COM component from the same apartment as the component.

SEE ALSO

Gotcha #68, "Default apartment of main thread is inconsistent across languages," Gotcha #69, "STAThread attribute may have no effect on your methods," and Gotcha #70, "Spattering access to COM components makes code hard to maintain."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net