COM Sample (1) - "COM Thread Model" Basics I have written a "COM thread model", which is favored, but some people think it is somewhat deep, hoping to provide some basic points of articles. This series of articles is the example of the article "COM Thread Model", providing a simple and complete example to help understand the content in the previous article. This article is the first one of this series. I explain that I have seen the foundation concept that may need to be described in the "COM Thread Model", which describes the structure of the sample in successive articles, and gives the relevant partial code to note as Mainly explained the means. The thread thread is a logical concept with process initiative. The so-called ability to indicate the characteristics of something to move according to a certain rule, and the so-called "moving" is a change in the state of "something". Then, even if the process runs in a certain rule, the "some rule" here is the definition of the CPU's machine instruction. The thread can be simply regarded as a worker that manipulates the machine, and the machine is equivalent to computer hardware, and the work of this worker is code. After work is made by the boss, wait for the workers to do. No one is doing things like the code, there is no meaning. When a worker is working to work, it is equivalent to a thread through the CPU execution process. Because the blanks change its state to change its state, the process changes to the product, and the process changes the state of the memory it has, which will change the state in which the CPU executes the code in the process (accurately saying the CPU response to the specified number). The calculation results are shown. Threads are equivalent to time, and time is a logical concept with objective world, which changes the objective world to the highly desperate laws of the world. The thread changes the content (including code) in the process to change the status of the process (the value of the memory) of the process of the machine instruction of the CPU. The thread handle and thread should be distinguished. The thread handle is an object, that is, a structure, which is determined by the operating system and must be associated with a thread. It is equivalent to a log record, which has no relationship, just a state of recording its related thread, if the thread is waiting, thread message queue, etc., the operating system operates on threads using the thread handle, such as sending thread messages , Waiting for the thread end, etc. Since the threader is just an object, it is equivalent to a record, so even if its associated thread has not existed (thread exits), it is also effective. It's like a person to die, but his ID card is still existing (although it is not meaningful). And even if the threaded handle is released through CloseHandle (accurately, only the reference count is reduced, because the operating system retains a reference to the thread, it does not mean that the thread is over, it is just a structure, record The related information of the thread is not related to the thread itself. Thread safety said that the thread is equivalent to a worker, you can operate a machine at both workers. These two workers must work together, and the A workers set up the machining speed of 10, and he has not started with 10 Processing speed processing parts, the B worker will set the processing speed for 20, if a does not know, this is very likely to cause an accident. At this time, we say that the machine's processing speed is not safe - anyone can change it without issuing any notifications. Therefore, it is specified that this machine can only be operated by one worker, and there are two workers to operate this machine. This synchronization means is implemented in only one thread in the program. This machine is more advanced, you can do a lot of things at the same time, if only one worker has too low.
Therefore, decide to make two workers to operate, but when setting up the processing speed, hang a brand at the setting handle, if a has set up the processing speed and is processing the parts, the brand is written in "use" words "use" After using it, write the brand in front of "unused". This achieves synchronization of processing speed settings. This is generally implemented by a mutex when Windows programming. Therefore, when multiple threads require a block memory in the process, the memory is the same as the "Machining Speed" above, and the synchronization means is required to prevent problems. This problem is generally called a thread conflict when there is a problem due to this reason. In addition to the thread conflicts mentioned above, there is a thread security issue called a deadlock (DEADLOCK). For example, the A worker is used in order to process III parts in the I component provided, and B is waiting for II part supplied by the A-machining to assemble I part. Because there is no other person to help communicate or other communication means (such as waiting for the past), they are waiting for the other side to wait for the phone because the parts are working well, will always Will not wait until the phone becomes a dead office. This is a deadlock, the two sides wait, this is a logical error, not like the above thread conflict, can be solved by a fixed method, which is a mistake made when the design algorithm is made, when the thread is more, and the relationship is complex It is easy to generate, can help check by assertion. Therefore, a piece of code is called thread security, means that this code does not happen or a deadlock. Thread switching general PCs are only one CPU, and the operation of multiple threads makes the computer seem to do a lot of things. This is to do this will be done, and another thing will be achieved. That is, the operating system frequently enables multiple threads to perform a short period of time (called time slice) to expect to perform multiple threads simultaneously through sufficient frequent. When a thread is performed in an execution, the operating system will enable another thread to run through some operations, while the former stops running, "some operations" is the so-called thread switching. The specific operation is to save the current thread's runtime, such as the value of the CPU register to a TLS memory, and then the thread of the thread will be run to the current CPU hardware Let the thread run, so the thread switch is the saving and load of the thread running environment. In the "COM Thread Model", the thread switching is very time consuming, but the operating system has extremely frequently changed, then the operating system is not very efficient? Not. The time-consuming time consumption of thread switches in the "COM Thread Model" is not a thread switch itself, but the thread is running mode conversion (from the user mode to the kernel mode), this operation generally has more than 1,000 CPU cycles, This is the so-called time consumption. Threads in Windows can run in two ways, kernel mode, and user mode. Under the kernel mode, it is possible to directly access physical memory, not the virtual memory space of the process, and there are multiple privileges. At this time, if an error occurs, it may be that the entire operating system is suspended (HALT) instead of simply use task manager (or similar software). This is generally the mode of work of kernel code or hardware driver. The user mode is a normal operation mode, and can only access physical memory through the mapping of the virtual memory space of the process, no privilege.
Windows has three objects: User Object, GDI Object (GDI Object), and kernel object. The memory used by the kernel object is the memory that can be accessed in kernel mode. Windows provides a range of APIs for each kernel object to operate. When calling such an API, the thread must be converted into a kernel mode to operate the memory of the kernel object, which results in the foregoing losses. When we run a thread synchronization, a waiting function similar to WaitForsingleObject is called. At this time, the thread will hang, waiting for the specified kernel object to be notified, at which time the above loss occurs. The thread synchronization function provided by COM default is destined to use the kernel between the STA thread and the MTA thread and the respective synchronization between the STA thread and the MTA thread. Synchronous objects (such as events, mutual exclusion, etc.) are synchronized, which also leads to the losses mentioned above, that is, the reasons and purpose of NA suite. Thread Local Storage (TLS) is a technology provided by Windows, which is used to associate some memory and a thread, so that even if the same code, different threads are accessed, it will actually access different Memory, this is the same as the thread's stack. Then why don't you use the stack to come to TLS? Because the stack is equivalent to a history, the memory value is closely related to the call order. If you want to share a memory between multiple functions, global variables should be used, but due to the thread correlation, decide to assign memory to the stack (but this is not important), you must accurately know that memory allocation in the stack when performing a function. What position (that is, address), and this location needs to pass another global variable to pass between functions, so there is a memory on the stack, and ... This is a dead cycle, which is why there is TLS existence. TLS has a total of 4 APIs, which are TLSalloc, TLSSetValue, TLSGetValue, and TLSFree. Calling TLSalloc will get a cookie, a DWORD value, a serial number. Then allocate a memory, save the address of the memory via TLSSetValue and the previous cookies, then call TLSGetValue to get the recorded address when appropriate, and the program is no longer used to call TLSFree to release the front cookie. The key above is that different thread calls TLSGetValue, even if the same cookie is provided, the return is not the same value. Similarly, different threads call TLSSetValue, even if the same cookie is not interfere with each other, and it can return correctly when TLSGetValude. Therefore, the top of the cookie can be a global variable. Such as: # include # include
/ / For example, do not do any error check and processing
DWORD G_COOKIE =
Static_cast
DWORD G_INDEX = 1;
Struct ABCD {
Long a;
Void CBA ()
{
Reinterpret_cast
SLEEP (Rand ()% 1000);
}
DWORD WINAPI AB (LPVOID)
{
ABCD * PTEMP =
NEW ABCD;
TLSSetValue (g_cookie, ptemp);
CBA ();
Printf ("% D / N", PTEMP-> a);
DELETE PTEMP;
Return 0;
}
void main ()
{
g_cookie = tlsalloc ();
DWORD ID =
Static_cast
Handle Hthreads [4];
FOR
unsigned
Long i = 0; i <4; i)
HThreads [i] = CreateThread (NULL, 0, AB, NULL, 0, & ID);
WaitFormultiPleObjects (4, Hthreads, True, Infinite);
For (i = 0; i <4; i)
CloseHandle (HThreads [I]);
TLSFree (g_cookie);
}
The output is as follows (note that there is no order because of g_index ):
1
3
2
4
The neutral form "COM Thread Model" of the interface pointer has been explained that the interface pointer is a thread correlation, although logically points to the same object, although different threads actually obtain different interface pointers due to the reasons of the agent object. However, due to logic is the same object, there should be a neutral form of an interface, independent of the thread, uniquely representing a real interface pointer. When we call ComarshalinterthreadInterfaceInstream to get an istream *, you can get a getting out of the interface from ISTREAM * from iStream * in the future. Here is ISTREAM * is a neutral form of the interface pointer. But if you really want to save iStream * this neutral form, you should call CounmarshalInterface to prevent release of iStream *. The front is a bit trouble, COM provides an object for this, called global interface table (Global Interface Table - Git), which implements the IGLOBALINTERFACETABLE interface to provide a package of the above steps. The IDs of their component classes and interfaces are CLSID_STDGLOBALINTERFACETABLE and IID_IGLOBALINTERFATABLE. Use this cookie to represent the registered interface using this cookie in the successful call of IGLOBALINTERFACETABAL and returns a cookie. Here, this cookie is the neutral form of the interface. The other two methods in IglobalInterFaceTable are RevokeInterfaceFromGlobal and GetInterfaceFromGlobal, which are used to log out the interface and get the correct interface pointer according to cookie.
The following is provided with a sample of git to explain how to record the neutral form instead of a direct interface, and demonstrate the use of iStream * in the code given in this series of subsequent articles (hypothesis CABCD implements a COM component, ABC and CBA are methods in an interface therefor.): Extern IglobalInterfaceTable * g_pgit; stdmethodimp Cabcd :: ABC (IABC * PABC) {if (! PABC) Return E_POINTER; // Do need processing, then retain PABC For later use, such as callback // use member variables DWORD M_DWABC to save IABC *, as follows: assert (g_pgit); if (failed (g_pgit-> registerinterface means)) Return E_FAIL; Return S_OK;} stdmethodimp Cabcd :: CBA () {if (m_dwabc == static_cast