We know that there are two ways to create a thread under Windows, one is to invoke the Windows API CreateThread () to create the thread, and the other is to invoke the function _beginthread () or _beginthreadex of the msvc CRT ( ) to create the thread. The corresponding exit thread also has the ExitThread () of the two function Windows APIs and the _endthread () of the CRT. These two sets of functions are used to create and exit threads, what's the difference?
Many developers are not clear about the relationship between the two, they choose a function to use, found that there is no big problem, so busy to solve more urgent tasks to go, but did not delve into them. Wait until one day suddenly found a program running for a long time there will be subtle memory leaks, developers will never think because of these two sets of functions mixed results.
Depending on the relationship between the Windows API and the MSVC CRT, you can see that _beginthread () is a wrapper over the CreateThread (), which ultimately calls CreateThread () to create the thread. So what do you do before _beginthread () calls CreateThread ()? We can look at the source code for _beginthread (), which is located in the CRT source code THREAD.C. We can see that it applied for a struct called _tiddata before calling CreateThread (), and then passed the struct to _beginthread () its own thread entry function _threadstart after it was initialized with the _INITPTD () function. _threadstart first saves the _TIDDATA structure pointer passed by _beginthread () to the thread's explicit TLS array, and then it invokes the user's thread entry to actually start the thread. After the user thread finishes, the _threadstart () function calls the _endthread () end thread. And _threadstart also uses __try/__except to wrap the user thread entry functions to capture all the unhandled signals and hand them over to the CRT for processing.
So in addition to the signal, it's obvious that the main purpose of the CRT wrapper Windows API thread interface is that _tiddata. What is stored in this thread's proprietary structure? We can find its definition in mtdll.h, which holds information such as the thread ID, thread handle, Erron, Strtok () 's previous call position, the rand () function's seed, exception handling, and so on, and is thread-private. The MSVC CRT does not define thread-private variables by using __declspec (thread), which we described earlier, to prevent library functions from being invalidated by multithreading, but rather to apply a _tiddata structure on the heap, placing thread-private variables inside the structure, A pointer to a _tiddata saved by an explicit TLS.
With this information in mind, we should think of a problem, which is that if we create a thread with CreateThread () and then call the CRT's Strtok () function, we should have an error, because the _tiddata that strtok () does not exist, But we never seem to have encountered such a problem. Looking at the Strtok () function, you will find that when you first call _GETPTD () to get the _tiddata structure of the thread, this function will request this structure and be responsible for initializing it if it finds that the thread has not applied for the _tiddata structure. So no matter what function we invoke to create the thread, we can safely invoke all functions that need to be _tiddata, because once the structure does not exist, it is created.
So when will _tiddata be released? ExitThread () certainly does not, because it does not know that there is _tiddata such a structure exists, then obviously is _endthread () release, this is also the CRT practice. However, we often find that even with CreateThread () and ExitThread () (without invoking the ExitThread () to exit the thread function in the same effect), no memory leaks are found, and why? After careful examination, we found the original password in the CRT DLL's entry function DllMain. We know that when a process/thread starts or exits, the DllMain of each DLL is invoked once, so the dynamically linked version of the CRT has the opportunity to release the thread's _tiddata in the DllMain. But DllMain only when the CRT is a dynamic link version of the role, static link CRT is not DllMain! This is a situation in which a memory leak is caused by the use of CreateThread (), in which case the _tiddata cannot be released at the end of the thread, causing the leak.
We can use the following small program to test:
Copy Code code as follows:
#include <Windows.h>
#include <process.h>
void thread (void *a)
{
char* r = strtok ("AAA", "B");
ExitThread (0); It doesn't matter if this function is invoked.
}
int main (int argc, char* argv[])
{
while (1) {
CreateThread (0, 0, (lpthread_start_routine) thread, 0, 0, 0);
Sleep (5);
}
return 0;
}
If you are using a dynamically linked CRT (/MD,/MDD), you will not have a problem, but if you use a static link CRT (/MT,/MTD), you will see that the memory usage is constantly rising when you run the program and then observe it in the process manager, but if we put thread () The ExitThread () in the function can be changed to _endthread (), because _endthread () will release _tiddata ().
This problem can be summed up as: when using the CRT (basically all programs use the CRT), try to use _beginthread ()/_beginthreadex ()/_ Endthread ()/_endthreadex () the set of functions to create threads. In MFC, there is a similar set of functions that are AfxBeginThread () and AfxEndThread (), which, by analogy to the above principles, are thread wrapper functions at the MFC level that maintain the structure of the thread associated with MFC, when we use the MFC class library, Try to use the thread wrapper function it provides to ensure that the program is running correctly.