[Experiment + Analysis] Using CreateThread to create a thread trap, analysis of createthread
First, not many people will read my blog (I am entertaining myself and record my thoughts ~)
Second, if someone does not want to continue reading the long story, they can directly come to the ultimate conclusion:
Do not use CreateThread!
Post Code first
#include <iostream>#include <windows.h>DWORD WINAPI thread_func(LPVOID lpParameter){std::cout << "sub-thread is running..." << std::endl;return 0;}int main(){HANDLE handle = CreateThread(NULL, 0, thread_func, NULL, 0, 0);CloseHandle(handle);return 0;}
What is the running result? Print a sub-thread is running ...?
I stopped when I printed it in half. At first I thought it was CloseHandle forced termination, So I commented out the sentence CloseHandle (handle), and the result was not optimistic.
After the annotation is removed, the result remains unchanged and only one "s" is printed ".
After searching, I found a comparison between CreateThread and _ beginthreadex on the Internet. By the way, I also have this book Windows core programming, which is recommended.
I did not take a closer look. I will use the latter directly.
#include <iostream>#include <process.h>unsigned __stdcall thread_func(void* lpParameter){std::cout << "sub-thread is running..." << std::endl;return 0;}int main(){auto handle = _beginthreadex(NULL, 0, thread_func, NULL, 0, NULL);_endthreadex(handle);return 0;}
It seems that a newbie wants to get started with the book "VC in-depth explanation". It is really only suitable for using VC6 ...... (My environment is win7 64-bit, VS2015)
So, why? I found a good blog Series
Seckill multithreading Article 2 multithreading first intimate contact with CreateThread and _ beginthreadex essential differences
After analyzing the source code of _ beginthreadex, Visual Assist X cannot jump to the definition of _ beginthreadex. After twists and turns, I wrote a simple search folder for all the keywords that contain a certain keyword. c /. program of the cpp file, and then find the source code under the C: \ Program Files (x86) \ Windows Kits path. My specific path is C: \ Program Files (x86) \ Windows Kits \ 10 \ Source \ 10.0.10240.0 \ ucrt \ startup \ thread. cpp
Because the source code of the post is old and the code style is old, it is in thread. c (typical C-style code ). But simply put, _ beginthreadex calls the CreateThread function, but it does some minor and important work.
Simply, it creates struct _ tiddata to store the exclusive data (that is, thread local) of each thread, and associates the struct with the thread, and then the data is protected, data is also released.
The thread. cpp of Windows Kits 10 uses the modern C ++ programming style and provides code, which is quite elegant.
extern "C" uintptr_t __cdecl _beginthread( _beginthread_proc_type const procedure, unsigned int const stack_size, void* const context ){ _VALIDATE_RETURN(procedure != nullptr, EINVAL, reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE)); unique_thread_parameter parameter(create_thread_parameter(procedure, context)); if (!parameter) { return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE); } // We create the new thread in a suspended state so that we can update // the parameter structure with the thread handle. The newly created // thread is responsible for closing this handle. DWORD thread_id{}; HANDLE const thread_handle = CreateThread( nullptr, stack_size, thread_start<_beginthread_proc_type>, parameter.get(), CREATE_SUSPENDED, &thread_id); if (!thread_handle) { __acrt_errno_map_os_error(GetLastError()); return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE); } parameter.get()->_thread_handle = thread_handle; // Now we can start the thread... if (ResumeThread(thread_handle) == static_cast<DWORD>(-1)) { __acrt_errno_map_os_error(GetLastError()); return reinterpret_cast<uintptr_t>(INVALID_HANDLE_VALUE); } // If we successfully created the thread, the thread now owns its parameter: parameter.detach(); return reinterpret_cast<uintptr_t>(thread_handle);}
Do not go into details, because it calls many other functions and uses them as pseudo code for reading.
First, create the unique_thread_parameter object after checking the validity of the returned value, and then check whether it is null. If it is null, return an invalid HANDLE.
Call CreateThread () to create a thread and return HANDLE to thread_handle.
Then, the unique_thread_parameter object is obtained with get (), and thread_handle returned by CreateThread () is assigned a value to point the pointer to the _ thread_handle field of the object, completing the connection between the thread parameter and the thread handle itself.
Add the previous code using unique_thread_parameter = _ crt_unique_heap_ptr <__acrt_thread_parameter,Thread_parameter_free_policy>;
Those who are familiar with C ++ 11 will soon be able to understand that from the perspective of naming, __crt_unique_heap_ptr and dedicated smart pointers unique_ptr are available in CRT (C-Run Time) and Heap) on the "extension" (similar to this, did not continue to study ). When the CRT is running C, the heap is used to dynamically apply for memory, which is equivalent to encapsulating the C-style memory management to make it safer, use the smart pointer RAII to automatically release resources, reduces the ugly C-style code (for example, the typical C-style application and memory release of _ initptd and _ free_crt in the source code of the second kill multithreading blog ).
Continue with the experiment. What if I add a cout output to the main function?
PS: two line breaks are output in the area where the graph is not captured, indicating that the line breaks are not lost.
What is the output result of the sub-thread appended to the output of the main thread?
What if I change std: endl to "\ n?
This is equivalent to accumulating the results of the separate sub-thread execution and the results of the separate main thread execution. Why is the difference between std: endl and "\ n?
template<class _Elem,class _Traits> inlinebasic_ostream<_Elem, _Traits>&__CLRCALL_OR_CDECL endl(basic_ostream<_Elem, _Traits>& _Ostr){// insert newline and flush stream_Ostr.put(_Ostr.widen('\n'));_Ostr.flush();return (_Ostr);}
Because std: endl not only adds a line break to the basic_ostream (output stream) object _ Ostr, but also refreshes the cache (flush ())
And so on. The output stream object ...... No ...... Std: cout?
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cout, *_Ptr_cout;
View the cout definition in <iostream>. It is a global ostream (that is, the basic_ostream template is instantiated using char) object.
typedef basic_ostream<char, char_traits<char> > ostream;
Global, global, global, and important things are said three times!
Review the second kill multithreading blog. It is precisely because global variables are vulnerable to interference from other threads that _ beginthreadex is used to create local variables of the thread, which are private to each thread, to prevent access conflicts. Similarly, using printf is not secure because it uses the global variable stdout.
The second line does not output ~~~
Go to the Baidu Encyclopedia of CreateThread and refer to the memory leakage caused by this function.
---------------------------------------------------------------------- Split line at the end ----------------------------------------------------------------------
After talking so much about it, I may still feel very blind, but it is clear that the water in CreateThread is very deep (so I just tried it ). However, there are at least a few experiences to summarize:
1. When using CreateThread to create a thread, try to use the Windows API library function instead of the C/C ++ library function that frequently uses global variables (in layman's terms, it is quite common );
2. If programming is performed in the C/C ++ environment, use _ beginthreadex and _ endthreadex instead of CreateThread and CloseHandle;
3. If the compiler supports C ++ 11, use the standard library header file <thread> and std: thread class starting with C ++ 11, which is simple and secure.
See http://stackoverflow.com/questions/25221247/c11-stdthread-vs-windows-createthread
In fact, the 2nd point is a false proposition-When does it use win api instead of programming in the C/C ++ environment? Other advanced languages, let alone their respective thread classes, are well encapsulated.
Since we don't need to use it, do we still need to study how deep it is?
The most refined Summary: Do not use CreateThread