This article will guide you through your first encounter with multithreading and delve deeply into the essential differences between CreateThread and _beginthreadex. After reading this, you'll be able to confidently use multithreading and accurately explain the distinctions between these two functions. The question is: Should you use CreateThread or _beginthreadex in real-world programming?
Multithreading is actually quite simple. The main thread of the following program creates a child thread and waits for it to finish. The child thread outputs its thread ID and prints a classic message — "Hello World." The entire program is short and easy to understand.
```cpp
// The simplest way to create a multi-threaded example
#include
#include
// Thread function
DWORD WINAPI ThreadFun(LPVOID pM)
{
printf("The thread ID of the child thread is: %d, child thread output Hello World", GetCurrentThreadId());
return 0;
}
// Main function, which is actually 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 output is as follows:

Now, let's take a closer look at some of the functions in the code.
First, `CreateThread`:
**Function:** Creates a thread
**Prototype:**
```cpp
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
```
**Description:**
- The first parameter specifies the security attributes of the thread object. Passing `NULL` uses default settings.
- The second parameter defines the stack size. A value of 0 means using the default size (typically 1MB).
- The third parameter is the address of the function that the new thread will execute.
- The fourth parameter is the argument passed to the thread function.
- The fifth parameter sets flags for thread creation. A value of 0 means the thread starts immediately; `CREATE_SUSPENDED` suspends the thread until `ResumeThread()` is called.
- The sixth parameter returns the thread ID, or `NULL` if not needed.
**Return Value:** Returns a handle to the new thread on success, or `NULL` on failure.
Next, `WaitForSingleObject`:
**Function:** Waits for a kernel object to be signaled.
**Prototype:**
```cpp
DWORD WINAPI WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
```
**Description:**
- The first parameter is the kernel object to wait for.
- The second parameter is the maximum time to wait, in milliseconds. Use `INFINITE` to wait indefinitely.
When the thread finishes execution, its handle becomes signaled, allowing `WaitForSingleObject()` to proceed.
Now, while `CreateThread` is a Windows API, C/C++ also provides `_beginthreadex()`. Many books, including *Windows Core Programming*, recommend using `_beginthreadex()` instead of `CreateThread`. Why? Let’s explore.
One key difference lies in how each handles the standard C runtime library. The C runtime was designed before multithreading support was common. As a result, global variables like `errno` can cause issues when accessed from multiple threads. For example, if one thread calls `system()` and another modifies `errno`, the first thread may read incorrect data.
To solve this, Windows provides a solution: each thread has its own memory area for CRT functions. This is managed by `_beginthreadex()`. Below is a simplified version of its source code with comments to help clarify the distinction between `_beginthreadex()` and `CreateThread()`:
```cpp
// _beginthreadex source code (simplified)
uintptr_t __cdecl _beginthreadex(
void *security,
unsigned stacksize,
unsigned (__CLR_OR_STD_CALL *initialcode)(void *),
void *argument,
unsigned createflag,
unsigned *thrdaddr
)
{
// Allocate and initialize per-thread data
_ptiddata ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata));
_initptd(ptd, _getptd()->ptlocinfo);
// Set up thread parameters
ptd->_initaddr = (void *)initialcode;
ptd->_initarg = argument;
// Call CreateThread internally
HANDLE handle = CreateThread(...);
return (uintptr_t)handle;
}
```
As seen, `_beginthreadex()` allocates a `_tiddata` block for each thread, ensuring that CRT functions like `strtok()` or `errno` are thread-safe. This makes `_beginthreadex()` the safer choice when working with the standard C library.
Let’s now create multiple threads using `_beginthreadex()`:
```cpp
// Creating multiple child thread instances
#include
#include
#include
unsigned int __stdcall ThreadFun(PVOID pM)
{
printf("The child thread whose thread ID is %4d says: Hello World", GetCurrentThreadId());
return 0;
}
int main()
{
printf("Create multiple child thread instances");
printf(" -- by MoreWindows (http://blog.csdn.net/MoreWindows) --");
const int 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;
}
```
Each thread prints "Hello World" with its own ID. Now, what if we want each thread to print a unique number? One approach is to use a global variable, but this leads to race conditions in a multithreaded environment. We’ll explore this further in the next section.
Shenzhen ChengRong Technology Co.,Ltd. , https://www.laptopstandsupplier.com