Understand multithreading and delve into the essential difference between CreateThread and _beginthreadex

This article will lead you to the first close contact with multi-threading, and deeply analyze the essential difference between CreateThread and _beginthreadex. I believe that after reading this article, you can easily use multi-threading and can smoothly and accurately answer the difference between CreateThread and _beginthreadex. Should I use CreateThread or _beginthreadex in actual programming?

Using multithreading is actually very easy. The main thread of the following program will create a child thread and wait for it to finish running. The child thread will output its thread ID number and output a classic quote - Hello World. The code for the entire program is very short, only a few lines.

[cpp] view plain copy

/ / The easiest way to create a multi-threaded instance

#include

#include

//sub-thread function

DWORD WINAPI ThreadFun(LPVOID pM)

{

Printf ("The thread ID number of the child thread is: %d child thread output Hello World", GetCurrentThreadId());

Return 0;

}

/ / The main function, the so-called main function is actually the function executed by the main thread.

Int main()

{

Printf("the easiest way to create a multi-threaded instance");

Printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --");

HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);

WaitForSingleObject(handle, INFINITE);

Return 0;

}

The result of the operation is as follows:

Understand multithreading and delve into the essential difference between CreateThread and _beginthreadex

Let's take a closer look at some of the functions in the code.

First CreateThread

Function: Create a thread

Function prototype:

HANDLEWINAPICreateThread(

LPSECURITY_ATTRIBUTESlpThreadAttributes,

SIZE_TdwStackSize,

LPTHREAD_START_ROUTINElpStartAddress,

LPVOIDlpParameter,

DWORDdwCreationFlags,

LPDWORDlpThreadId

);

Function description:

The first parameter indicates the security attribute of the thread kernel object. Generally, passing NULL means using the default settings.

The second parameter indicates the size of the thread stack space. Passing 0 means using the default size (1MB).

The third parameter represents the thread function address executed by the new thread, and multiple threads can use the same function address.

The fourth parameter is the argument passed to the thread function.

The fifth parameter specifies an additional flag to control thread creation. A value of 0 means that the thread can be dispatched immediately after it is created. If it is CREATE_SUSPENDED, it means that the thread is suspended after it is created, so that it cannot be dispatched until ResumeThread() is called.

The sixth parameter will return the thread ID number, and passing NULL means that the thread ID number does not need to be returned.

Function return value:

The handle of the new thread was successfully returned, and the failure returned NULL.

Second WaitForSingleObject

Function: Wait for function – Puts the thread into a wait state until the specified kernel object is triggered.

Function prototype:

DWORDWINAPIWaitForSingleObject(

HANDLEhHandle,

DWORDdwMilliseconds

);

Function description:

The first parameter is the kernel object to wait for.

The second parameter is the longest waiting time, in milliseconds. If you pass 5000, it means 5 seconds. If you pass 0, it will return immediately. If you pass INFINITE, it will wait indefinitely.

Because the handle of the thread is not triggered when the thread is running, the thread ends running and the handle is in the triggered state. So you can use WaitForSingleObject() to wait for a thread to finish running.

Function return value:

The object is fired within the specified time and the function returns WAIT_OBJECT_0. The object that has passed the longest wait time has not been triggered yet returns WAIT_TIMEOUT. An error in the incoming parameter will return WAIT_FAILED

The CreateThread() function is an API interface provided by Windows. In the C/C++ language, there is another function that creates threads _beginthreadex(). In many books (including "Windows Core Programming"), I have tried to use _beginthreadex(). Instead of using CreateThread(), why is this? Let's explore and discover the difference between them.

First of all, from the contradiction between the standard C runtime and multithreading, the standard C runtime was implemented in 1970, because no operating system provided support for multithreading at the time. So the programmer writing the standard C runtime does not consider the case of multithreaded programs using the standard C runtime. For example, the global variable errno of the standard C runtime library. Many functions in the runtime will assign error codes to this global variable when an error occurs, which is convenient for debugging. But if there is such a code snippet:

[cpp] view plain copy

If (system("notepad.exe readme.txt") == -1)

{

Switch(errno)

{

...//error handling code

}

}

Suppose a thread A is executing the above code. After the thread calls system() and the switch() statement has not been called, another thread B is started. This thread B also calls the function of the standard C runtime library. Unfortunately, This function performs an error and writes the error code to the global variable errno. Thus, once thread A begins executing the switch() statement, it will access an errno that has been modified by the B thread. This situation must be avoided! Because not only this variable will cause problems, other functions like strerror(), strtok(), tmpnam(), gmtime(), asctime() will also encounter such data coverage caused by multiple thread access modifications. problem.

To solve this problem, the Windows operating system provides such a solution - each thread will have its own dedicated memory area for all the functions needed in the standard C runtime. And the creation of this memory area is responsible for the C / C + + runtime function _beginthreadex (). The source code for the _beginthreadex() function is listed below (I added some comments to this code) to give the reader a better understanding of the difference between the _beginthreadex() function and the CreateThread() function.

[cpp] view plain copy

//_beginthreadex source code to complete By MoreWindows (http://blog.csdn.net/MoreWindows)

_MCRTIMP uintptr_t __cdecl _beginthreadex(

Void *security,

Unsigned stacksize,

Unsigned (__CLR_OR_STD_CALL * initialcode) (void *),

Void * argument,

Unsigned createflag,

Unsigned *thrdaddr

)

{

_ptiddata ptd; //pointer to per-thread data See note 1

Uintptr_t thdl; //thread handle thread handle

Unsigned long err = 0L; //Return from GetLastError()

Unsigned dummyid; //dummy returned thread ID thread ID number

// validation section checks if initialcode is NULL

_VALIDATE_RETURN(initialcode != NULL, EINVAL, 0);

//Initialize FlsGetValue function pointer

__set_flsgetvalue();

//Allocate and initialize a per-thread data structure for the to-be-created thread.

/ / Equivalent to a new _tiddata structure, and assigned to the _ptiddata pointer.

If ( (ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL )

Goto error_return;

// Initialize the per-thread data

/ / Initialize the thread's _tiddata block is the CRT data area See Note 2

_initptd(ptd, _getptd()->ptlocinfo);

//Set other data in the _tiddata structure so that the _tiddata block is associated with the thread.

Ptd->_initaddr = (void *) initialcode; //thread function address

Ptd->_initarg = argument; //incoming thread parameters

Ptd->_thandle = (uintptr_t)(-1);

#if defined (_M_CEE) || defined (MRTDLL)

If(!_getdomain(&(ptd->__initDomain))) // see note 3

{

Goto error_return;

}

#endif //defined (_M_CEE) ||defined (MRTDLL)

// Make sure non-NULL thrdaddr is passed to CreateThread

If ( thrdaddr == NULL ) / / determine whether you need to return the thread ID number

Thrdaddr = &dummyid;

// Create the new thread using the parameters supplied by the caller.

//_beginthreadex() will eventually call CreateThread() to request the system to create a thread.

If ( (thdl = (uintptr_t)CreateThread(

(LPSECURITY_ATTRIBUTES) security,

Stacksize,

_threadstartex,

(LPVOID)ptd,

Createflag,

(LPDWORD)thrdaddr))

== (uintptr_t)0 )

{

Err = GetLastError();

Goto error_return;

}

//Good return

Return(thdl); //The thread is created successfully, returning the handle of the new thread.

//Error return

Error_return:

//Either ptd is NULL, or it points to the no-longer-necessary block

//calloc-ed for the _tiddata struct which should now be freed up.

/ / Recycle the _tiddata block requested by _calloc_crt ()

_free_crt(ptd);

// Map the error, if necessary.

// Note: this routine returns 0 for failure, just like the Win32

// API CreateThread, but _beginthread() returns -1 for failure.

/ / Correct the error code (you can call GetLastError () to get the error code)

If ( err != 0L )

_dosmaperr(err);

Return( (uintptr_t)0 ); //Return a valid handle with a value of NULL

}

Explain the next part of the code:

Note 1. _ptiddata in _ptiddataptd; is a structure pointer. The mtdll.h file is defined:

Typedefstruct_tiddata * _ptiddata

Microsoft commented on it as Structure for each thread's data. This is a very large structure with many members. This article is not listed due to space limitations.

Note 2. _initptd(ptd, _getptd()->ptlocinfo); Microsoft's description of getptd() in this code is:

/* return address of per-thread CRT data */

_ptiddata __cdecl_getptd(void);

The description of _initptd() is as follows:

/* initialize a per-thread CRT data block */

Void__cdecl_initptd(_Inout_ _ptiddata _Ptd,_In_opt_ pthreadlocinfo _Locale);

The CRT (C Runtime Library) in the comment is the standard C runtime library.

Note 3. The _getdomain() function code in if(!_getdomain(&(ptd->__initDomain))) can be found in the thread.c file. Its main function is to initialize the COM environment.

As you can see from the source code above, the _beginthreadex() function allocates and initializes a _tiddata block when creating a new thread. This _tiddata block is naturally used to store some data that needs to be exclusive to the thread. In fact, the new thread will first associate the _tiddata block with itself. Then the new thread calls the standard C runtime function, such as strtok (), it will first get the address of the _tiddata block and then store the data to be protected into the _tiddata block. This way each thread will only access and modify its own data without tampering with the data of other threads. Therefore, if you have a function in the standard C runtime in your code, try to use _beginthreadex() instead of CreateThread(). I believe that when you read this, you will be very impressed with this short sentence. If an interviewer asks, you can answer ^_^ smoothly and accurately.

Next, similar to the above program to create a child thread that outputs "Hello World" with CreateThread(), use _beginthreadex() to create multiple child threads:

[cpp] view plain copy

/ / Create multiple child thread instances

#include

#include

#include

//sub-thread function

Unsigned int __stdcall ThreadFun(PVOID pM)

{

Printf ("The child thread whose thread ID is %4d says: Hello World", GetCurrentThreadId());

Return 0;

}

/ / The main function, the so-called main function is actually the function executed by the main thread.

Int main()

{

Printf("Create multiple child thread instances");

Printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --");

Constint THREAD_NUM = 5;

HANDLE handle[THREAD_NUM];

For (int i = 0; i < THREAD_NUM; i++)

Handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);

WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

Return 0;

}

The results are as follows:

Understand multithreading and delve into the essential difference between CreateThread and _beginthreadex

Each sub-thread in the picture says the same sentence, not very good-looking. Can you come to a thread count function, that is, the first child thread outputs 1, the second child thread outputs 2, and the third child thread outputs 3, .... It seems very simple to implement this function - each child thread increments and outputs a global variable. code show as below:

[cpp] view plain copy

//Sub-thread count

#include

#include

#include

Int g_nCount;

//sub-thread function

Unsigned int __stdcall ThreadFun(PVOID pM)

{

g_nCount++;

Printf("Thread ID number %d for child thread %d", GetCurrentThreadId(), g_nCount);

Return 0;

}

/ / The main function, the so-called main function is actually the function executed by the main thread.

Int main()

{

Printf("sub-threaded report");

Printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --");

Constint THREAD_NUM = 10;

HANDLE handle[THREAD_NUM];

g_nCount = 0;

For (int i = 0; i < THREAD_NUM; i++)

Handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);

WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

Return 0;

}

A screenshot of the results of one run is as follows:

Understand multithreading and delve into the essential difference between CreateThread and _beginthreadex

Showing results from 1 to 10 seems to be no problem.

The answer is wrong. Although this approach is logically correct, doing so in a multi-threaded environment can create serious problems.

HDMI Cables

Hdmi Cables,Hdmi Cord,Micro Hdmi Cable,Extra Long Hdmi Cable

UCOAX , https://www.jsucoax.com

This entry was posted in on