Details about the com thread model

Source: Internet
Author: User
This article explains the various types of thread models proposed by COM, and explains how the com Runtime Library implements them.

The thread model is a mathematical model provided specifically for multithreaded programming.AlgorithmBut it is only an algorithm, not an implementation. This article explains the various types of thread models proposed by COM, and then describes how the com Runtime Library implements them, just like how Windows implements the thread mathematical model, finally, the requirements for cross-Suite calls and various types of suites are specified to help you understand. I hope that you will be familiar with the concept of a Windows operating system thread and have a good understanding of what thread security means.

Com thread model

There are three types of thread models provided by COM: Single-threaded apartment (STA single-thread suite) and multithreaded apartment (MTA multi-thread suite) and neutral apartment/thread neutral apartment/Neutral threaded apartment (Na/TNA/NTA neutral thread suite, provided by COM + ). Although their names all contain the term "Suite", this is only the com Runtime Library (note, not the COM specification, or "com") that uses the suite technology to implement the preceding three thread models, it should be noted that the suite and thread model are not the same concept. There are three types of suites provided by COM, which correspond one to one. The existence of the thread model is caused by different thread rules, and there are only two so-called thread rules:CodeIt is thread-safe or insecure, that is, access conflicts will not occur when the Code accesses public data. Since the thread model is only a model, it can be broken in terms of concept, but it cannot obtain the advantages of automatic synchronous calling and compatibility provided by COM.

Sta an object can only be accessed by one thread (its method is called through the interface pointer of the object). Other threads cannot access this object. Therefore, all calls to this object are synchronized, the object state (that is, the value of the member variable of the object) must be correct and will not cause errors in the object state due to thread access conflicts. To access this object, other threads must wait until the unique thread is idle. Note: This is only the requirement, hope, and Protocol. Whether or not it is actually done is determined by COM. As mentioned above, this model is similar to the window message running mechanism provided by windows. Therefore, this thread model is very suitable for interface components such as ActiveX controls and Ole document servers, all should use the sta suite.

An MTA object can be accessed by multiple threads. That is, the code of this object implements thread protection in its own method, ensuring that its status can be changed correctly. This is very suitable for components that serve as business logic components or dry background services. As a distributed server, thousands of service requests may arrive at the same time. If a request is called in queue, it cannot be imagined. Note: This is just a requirement, hope, and agreement.

An object in Na can be accessed by any thread. It is different from MTA in any thread. When it is accessed across suites (as described later), its call fee (CPU time and resources consumed) it is much less. This accuracy cannot be regarded as a thread model. It is a requirement based on the specific implementation of the suite. What is different from MTA is the com implementation method.
Com suite

Apartment is translated into a set of rooms or units, and is a real-time entity of the thread model. Just like the thread mentioned in the operating system course is just a mathematical model, windows threads and processes are the implementers of the threads and processes in the mathematical model. A suite is just a logical concept. It is only a structure (managed by com) to record relevant information, such as its type (only the three above can be used, at least for now), and the com will handle it according to that structure. The following describes how these three suites are implemented:

For a sta suite, if it is a STA, then that suite has only one thread associated with it, there are multiple objects or no objects associated with it, just as there are multiple threads associated with a process, that is to say, the structure of the suite is related to a thread and multiple objects. What is the relationship calculated by com, fortunately, COM defines the relationship between each other according to the preceding thread model. According to the above algorithm, it is easy to know that only this thread can access the objects in this suite.

Com creates a hidden window in the thread of the sta suite, and then calls from the outside (the thread outside the suite) to this object are converted to send messages to the hidden window, then, the message processing function of this hidden window is used to actually call the method of the component object to implement the sta rule. A hidden window is used to facilitate the compilation of component code. You only need to call dispatchmessage to distinguish the messages called by the method from common messages (by hiding the message processing function of the window ). The external calls to this object are converted into messages sent to this hidden window for synchronization. As for how com intercepts external calls to objects, it is conducive to proxy objects, which will be explained later.

It is worth noting that if the standard collection method is used to generate a proxy object, the proxy object will decide the specific operation based on the Cross-Suite call within or outside the process. If the external thread and the stathread are in the same process, the proxy object will directly send a message to the hidden window in the stathread. If they are not in the same process (including remote processes ), the proxy object requests a thread (RPC thread) to a thread pool managed by RPC to send messages to the hidden window of the stathread in another process, instead of directly sending messages to the proxy object, to prevent external threads from being suspended due to network instability.

Because com uses the message mechanism to implement the STA, the thread in the sta suite must implement the message loop. Otherwise, com cannot implement the requirements of the STA.

An MTA suite can be associated with multiple threads and multiple or no objects. Based on the MTA model above, we can see that only the threads in this suite can access the objects in this suite. What is different from the sta is that multiple threads can access the objects at the same time.

The call from the outside (not the thread of this suite) to the objects in this suite will cause the call thread (the external thread, that is, the stathread, because Na has no thread) to be suspended, request a thread (RPC thread, which has already entered the MTA suite) from a thread pool managed by RPC to call the method of that object. After the object is returned, the calling thread is awakened and continues to run. Although the stathread can directly call an object (instead of waiting for another thread to call the object like the preceding one), this is required because of callback problems, for example, the MTA thread calls back the component object in the external thread in turn (assuming that the customer is also a component object, this is the connection point technology). If asynchronous callback occurs, errors may occur.

In turn, when the MTA thread accesses objects in the STA, com will convert the call into a message sent from the hidden window in the stathread, after the result is returned, it is converted from com to the result returned to the MTA thread (if the standard collection method is used to generate the standard proxy object, the specific situation is as described in the sta suite above ). Therefore, Stas and MTA can only call their associated objects by their associated threads. As mentioned above, when the MTA calls the sta or sta call the MTA, thread switching will occur. That is to say, one thread is suspended and is switched to another thread. This is a considerable consumption (switching from the kernel mode to the user mode is required, and then reversing several times), and Na is designed for this purpose.

The NA suite is only associated with objects and has no associated threads. Therefore, any thread can directly access the objects in it. The STA or MTA does not exist.

The outside world (in fact, any thread) does not need to wait for calls in this suite, but enters the NA suite and calls the object method directly. The NA suite is provided by COM +. Each object in COM + has an environment bound to it. The environment records necessary information and listens to every call to the object, to ensure that the operation is correct when the object's interface pointer member variables are passed or called back (ensure that the execution thread is in the correct suite, the MTA thread is guaranteed by suspending itself to wait for the stathread to complete message processing), thus avoiding the suspension of the call thread. Therefore, this proxy (which is actually part of the environment) it is called a lightweight proxy (equivalent to the heavyweight proxy of the sta suite and the MTA suite-the call thread needs to be suspended and thread switching occurs ).

This lightweight proxy does not always have thread switching. When a na object calls a pointer to a sta object and the calling thread is not the Thread associated with the sta object, the call will be converted to sending a message to the associated thread of the called sta object, and thread switching will still occur. Similarly, if the object is MTA and the call thread is a stathread, thread switching still occurs. However, in most cases (that is, not calling the method of another suite object in the method of the NA object), thread switching will not occur, even in the above situation, the thread can be switched only when the MTA calls Na and then calls the MTA.

According to the above, the sta is actually exactly the same as the MTA logic, but one is to associate a thread, and the other is to associate multiple threads. But it is necessary to separate them, because thread security is for one thread or multiple threads. The reason why na does not associate threads is that it aims to eliminate the thread switching loss caused by the preceding cross-Suite call. The associated threads have no significance.

A process can have multiple Stas, but I can only have one MTA suite and one na suite. I think it is easy to understand (What do I need to do with two MTA suites or Na Suites ?).
Suite generation rules

Before a thread performs most COM operations, it must call coinitialize or coinitializeex. Call coinitialize to tell com to generate a sta suite and associate the current call thread with this suite. Call coinitializeex (null, coinit_multithreaded); tell com to check whether an MTA suite exists. If not, generate an MTA suite and associate the suite with the calling thread. Then, when you call functions such as cocreateinstance or cogetclassobject to create an object, the created object will be associated with a specific rule (subsequent description ). After this is done, the Association (or binding) of threads, objects, and suites is completed ).

The rules for determining the object destination mentioned above are as follows.

When it is an in-process component, it is listed in the following table based on the registry key <CLSID> \ inprocserver32 \ threadingmodel and thread:

Types of suites associated with the creation thread Threadingmodel key value The final suite of the Component Object
Sta Apartment Create a thread suite
Sta Free MTA suite in process
Sta Both Create a thread suite
Sta "" Or single Master sta suite in process
Sta Neutral In-process na suite
MTA Apartment A new sta suite
MTA Free MTA suite in process
MTA Both MTA suite in process
MTA "" Or single Master sta suite in process
MTA Neutral In-process na suite

The master sta suite in the process is the suite associated with the first thread that calls coinitialize in the process (that is, the first sta suite in the process ). The following explains why an in-process master sta suite is coming.

When it is an out-of-process component, the main function calls coinitializeex or coinitialize to specify the suite of the component. The same as above, coinitialize represents STA, coinitializeex (null, coinit_multithreaded); MTA, no Na. Because Na is provided by COM +, and the COM + service can only be provided to In-process servers, only the above registry key rules are used to determine whether the DLL component is put into the NA suite, coinitializeex (null, coinit_neutral); is not provided to process exe components. Moreover, if coinitializeex (null, coinit_neutral) can be used, the call thread is associated with the NA suite and violates the NA thread model, this is why the threadingmodel key is under <CLSID> \ inprocserver32.

Cross-Suite call

Stathread 1 creates a sta object, obtains the interface pointer iabcd *, then it initiates stathread 2, and passes iabcd * as the thread parameter. In thread 2, call the iabcd: ABC () method. The days of success or failure are doomed. Because the sta suite in which thread 2 is located is different from the sta suite in which thread 1 is located, thread 2 will call the object in another suite across the suite. According to the aforementioned sta rules, iabcd: ABC () should be converted into messages for sending. If the above method is used, it can be compiled and passed, but the running is not guaranteed.

The reason why com can implement the rules mentioned above (STA, MTA, and Na) is that when calling a cross-Suite call, the called object pointer is directed to a proxy object, not the component object itself. The proxy object implements the aforementioned three implementation algorithms (converted to message sending, thread switching, etc.), and generally referred to as the proxy/placeholder object (proxy/stub) and so on ). The pointer passed directly through the thread parameter above points directly to the object, so the sta rule cannot be implemented. Therefore, com provides the following two functions (there are other methods, for example, you can use the global interface table git to generate a proxy: codecomalinterface and counexternalinterface (if the interface pointer is transferred between threads in the same process, you can use these two functions to further simplify code writing: coexternalinterthreadinterfaceinstream and cogetinterfaceandreleasestream ).
Now rewrite the code above. After thread 1 gets iabcd *, call coexternalinterface to get an istream *, and then pass istream * to thread 2. In thread 2, call counexternalinterface to get iabcd *, now this iabcd * is directed to the proxy object, not the component object.

Therefore, all the preceding thread model algorithms are implemented through proxy objects. When you want to cross-suite, use coexternalinterface to set the CLSID of the proxy object and the necessary information (such as the interface pointer of the Component Object) that it establishes a connection with the component object) to an istream *, use any inter-thread communication means (such as global variables) to upload istream * to the thread to be used, and then use counw.alinterface (unmarshaling) to obtain the interface pointer to the proxy object. Therefore, we need to obtain the proxy Object Pointer because we want to use the thread model provided by COM (but in COM +, this is not the only reason ), if you don't want to use big data, you don't have to be so troublesome (but at your own risk), and you don't have to do that.

When both thread 1 and thread 2 are MTA, you can directly transfer iabcd * to thread 2 as described at the beginning, because the MTA thread model allows multiple threads to directly call objects at the same time, thread 1 and thread 2 are in the same MTA suite, and the object declares to com that it supports the MTA thread model in some form (such as threadingmodel = Free.

When both thread 1 of a.exeand thread 2 of B .exe are MTA, we still need to collect interface pointers (column set → transmission → scattered set) as above to get pointers pointing to the proxy rather than the object, even if thread 1 and thread 2 are both in the MTA suite, they are in two different MTA suites. Therefore, they are called across suites and need to be aggregated.

Code collection

As mentioned above, the suite rules are implemented by calling proxy objects instead of component objects to intercept calls to component objects. The proxy object needs to interact with the component object and pass the method parameters to the component object. It needs to use the collection technology, that is, the process of column set → transmission → scattered set.

A column set is an operation that stores information in a certain format as an istream * stream. An unmarshaling is a reverse operation of a column set, the information is reversed from the stream form, while the transfer is only a stream form transfer operation.

Misunderstandings often occur here. The column set made by codecomalinterface is to format the CLSID of the proxy object and some persistent information (used to initialize the proxy object) into a format (network data description-network data representation) and then put it in a stream object. You can pass the stream object to the client through the network (or other methods, the customer returns the CLSID of the proxy object and some persistent information for initialization from the stream object through counw.alinterface, generates the proxy object, and uses the persistent information to initialize it for collection operations. This is where the misunderstanding occurs-the collection operation here is different from the above collection operation, which collects the parameters of the interface method rather than the CLSID and some initialization information.

Therefore, codecomalinterface and counexternalinterface are used to collect interface pointers, and the precise points should be used to generate proxy objects. The proxy object should be implemented by the reader and used to collect interface method parameters. There are two methods to implement proxy objects: custom collection and standard collection.

For custom aggregation, the component must implement the imarshal interface and a proxy component (that is, a copy of all the interfaces of the real component is fully implemented, and the collection method parameters and thread model rules are implemented, and register the Proxy component on the client to ensure that the proxy object is correctly generated. NOTE: If an interface pointer exists in the parameter, codecomalinterface and counexternalinterface must be used for collection. Otherwise, the correct thread model cannot be implemented and the agent component is the real-name of the thread model, this component must be self-guaranteed (such as sending messages ).

For standard collection, components do not need to implement the imarshal interface and proxy components. Instead, components need to generate a proxy/placeholder component (proxy/stub) for themselves ), because it can be automatically generated by the IDL file through midl, it is highly efficient and the code correctness is ensured, so it is encouraged to use it. Com provides the implementation of a standard proxy object, which is represented as a proxy object of the component by the proxy/placeholder component of the aggregation component. Like a custom collection, You need to register the proxy/placeholder component on the client to ensure that the proxy object is correctly generated.

As for the specific working mechanism of these two collections, it is irrelevant to this article and is not listed here. Here, it is only to eliminate confusion between proxy objects and proxy/placeholder components.

Note: For components that will run on the NA suite, due to the mandatory requirements of COM +, it must use standard collections to generate proxy objects rather than custom collections (the COM + Runtime Library overwrites standard proxy objects to intercept calls to component objects and some special processing of their own -- to ensure that the NA suite works properly ).

Suite Implementation Rules

As mentioned above, the suite mechanism of COM must be successfully implemented by the combination of servers (components), customers, and COM runtime libraries. Either party does not follow the rules, the functions of the suite mechanism will not be implemented, but this does not mean anything wrong. The unavailability of the suite mechanism does not meanProgramWill crash, but it cannot be compatible with other COM applications.

For example, attribute 1 in an object will not be written by more than two threads in the design algorithm, but will be read by multiple threads at the same time. Therefore, you do not need to synchronize it. You can use MTA, however, attribute 2 of an object may be written by multiple threads, so Stas is used. In this way, the client transmits the object pointer to the thread that only writes attribute 1 of the object through the codecomalinterface and counexternalinterface mentioned earlier. In fact, the object pointer can be directly transmitted to this thread, it doesn't have to be as troublesome as above (and increases efficiency), but it breaks the com suite rules-two threads can access objects, but the objects are in the sta suite. So ?!! Nothing happens, because we already know exactly that this algorithm won't hit the blind spot (thread access conflicts), even if we break the com rules ?! In addition, components can still be compatible with other customers, because customers do not follow the rules and are irrelevant to components. However, if a component breaks the rules, it cannot be compatible with every customer, but it does not mean it is not compatible with any customer. In fact, the combination of the customer and component spoofs the com Runtime Library.

The above example only aims to help readers better understand the suite. In practice, we should try to maintain compatibility with the com standard (But incompatibility does not mean it is wrong ). The work that the customer has done has already been said (the two functions, the global interface table, or others, as long as they are in the correct way). The following describes the work that the component should do. Components can exist in four suites (one master sta suite is added). The work is as follows:

When a component is STA, it must synchronously protect global variables and static variables, that is, access to global variables and static variables should be protected by critical segments or other means of synchronization, because the code that operates global and static variables can be executed by multiple stathreads at the same time, the Code should be protected. For example, the object count (note, not the reference count) indicates the number of objects generated by the current component. When the value is reduced to zero, the component is uninstalled. This variable is generally used by the class factory object. Fortunately, ATL and MFC have helped us implement the default class factory, so We generally don't have to worry about it here, but the custom global or static variables have to be processed by ourselves.

The only difference between a master STA and a sta is that it is a silly model, and neither static nor global variables can be thread-protected, because all objects that are not securely accessing static and global variables run through the message delivery mechanism of the main thread (the first thread that calls coinitialize, therefore, insecure access is concentrated in a thread call. Therefore, the call is serialized, thus implementing thread protection for static and global variables. As for why the master thread is used, the master thread will be created because the process needs to use the STA, so the master sta can be created. Therefore, the master sta is not the fourth suite. It is just a sta suite, but it is associated with the main thread. It can be used to protect static and global variables and is proposed separately. Therefore, there is only one master sta suite in a process.

The MTA must use synchronous means to protect access to each member in the component and global and static variables. The thread issue should also be considered, that is, access can be simply protected, you should also pay attention to the operation errors caused by the thread. The most classic is iunknown: release ().

DWORD iunknown: release ()
{
DWORD temp = interlockeddecreament (& m_refcount );
If (! Temp) // m_refcount cannot be used. Please think for yourself
Delete this; // Therefore, it is not necessary to use an atomic access function to protect m_refcount access.
Return temp; // The protection of global variables is similar to that of global variables. Thread issues should be considered.
}

If you have no confidence in your multi-threaded programming technology, it is recommended that you do not write components that can exist in the MTA suite, but you cannot get the high performance of the MTA.

When writing MTA, you should also note the thread affinity (thread affinity ). No-thread affinity refers to member variables without any thread range, such as local thread storage (TLS) and window handle. In other words, the MTA cannot store any pointer or window handle that records the TLS memory. It makes no sense to save it (for example, the memory space recorded by thread a is invalid for thread B, because TLS constructs a thread-related memory space, just as each process has its own private space ). Unfortunately, MFC uses TLS extensively in its underlying operating mechanism, such as the module thread status and thread status. For this reason, MFC cannot compile components running in MTA.

Since multiple threads may access the objects in the NA suite at the same time, like MTA, Na does not have thread affinity and needs to protect each member and global and static variables. The lightweight proxy for Na is generated by the COM + Runtime Library, so you don't have to worry about it (you just need to assign the threadingmodel key value of that component to "neutral ).

As mentioned above, the threadingmodel key value of an in-process component can be assigned as "both", which is similar to NA and can be directly accessed by any suite, but only possible, the NA component is acceptable. This can be seen from the rule table of the suite to which the component in the previous process belongs. This component supports a technology called FTM -- free threaded already ALER, which is not listed here because it is irrelevant to the subject of this article. When the both component uses a free thread collector, in addition to meeting MTA requirements (the preceding thread security protection and no thread correlation ), it also records the neutral form of the passed interface pointer (for example, istream *, obtained through codecomallinterface) to prevent the customer's callback problems.

at last, I would like to remind you that there are three sta1 suites, sta1, sta2, and sta3. Sta1 uses the istream * obtained from codecomallinterface to pass to sta2 the proxy obtained through counexternalinterface is different from the proxy obtained through counexternalinterface in sta3, and cannot be mixed. Because when sta2 and sta3 call objects in sta1, if sta1 calls a callback (connection point technology is a callback, the proxies of sta2 and sta3 can correctly specify the thread to perform the callback operation, that is, the thread to which messages are sent. Therefore, they cannot be mixed.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.