Threads
This article provides an overview of creating and managing threads in Windows using the Win32 API.
Table of Contents
Creating Threads
Use CreateThread or _beginthreadex to start a new thread.
#include <windows.h>
#include <process.h>
unsigned __stdcall ThreadFunc(void* param)
{
// Thread code here
return 0;
}
int main()
{
HANDLE hThread = (HANDLE)_beginthreadex(
NULL, // security
0, // stack size
ThreadFunc, // start address
NULL, // parameter
0, // creation flags
NULL); // thread identifier
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return 0;
}
Thread Procedure
The thread function must match the required signature and should avoid calling non‑thread‑safe CRT functions unless using _beginthreadex.
Synchronization Primitives
Windows provides several synchronization objects to coordinate thread execution:
| Primitive | Header | Typical Use |
|---|---|---|
| Critical Section | Windows.h | Fast intra‑process lock |
| Mutex | Windows.h | Inter‑process synchronization |
| Event | Windows.h | Signaling between threads |
| Semaphore | Windows.h | Control access to limited resources |
| SRW Lock | Windows.h | Lightweight reader/writer lock |
Thread Lifecycle
The typical lifecycle includes:
- Creation –
CreateThreador_beginthreadex - Execution – runs the thread procedure
- Waiting –
WaitForSingleObject,WaitForMultipleObjects - Termination – thread exits or calls
ExitThread - Cleanup –
CloseHandleon the thread handle
Best Practices
- Avoid UI work on worker threads – use
PostMessageorSendMessageto marshal to the UI thread. - Prefer
_beginthreadexwhen using CRT functions. - Use
EnterCriticalSection/LeaveCriticalSectionfor fast intra‑process locking. - Release all synchronization objects in a
finallyblock or RAII wrapper. - Limit the number of active threads to avoid excessive context switching.
Sample: Producer‑Consumer Using Event
#include <windows.h>
#include <process.h>
#include <stdio.h>
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int count = 0;
HANDLE hProduceEvent;
HANDLE hConsumeEvent;
HANDLE hMutex;
unsigned __stdcall Producer(void*)
{
for (int i = 0; i < 10; ++i)
{
WaitForSingleObject(hMutex, INFINITE);
while (count == BUFFER_SIZE) // buffer full
WaitForSingleObject(hConsumeEvent, INFINITE);
buffer[count++] = i;
printf("Produced %d\n", i);
SetEvent(hProduceEvent);
ReleaseMutex(hMutex);
Sleep(100);
}
return 0;
}
unsigned __stdcall Consumer(void*)
{
for (int i = 0; i < 10; ++i)
{
WaitForSingleObject(hMutex, INFINITE);
while (count == 0) // buffer empty
WaitForSingleObject(hProduceEvent, INFINITE);
int val = buffer[--count];
printf("Consumed %d\n", val);
SetEvent(hConsumeEvent);
ReleaseMutex(hMutex);
Sleep(150);
}
return 0;
}
int main()
{
hMutex = CreateMutex(NULL, FALSE, NULL);
hProduceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hConsumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
_beginthreadex(NULL, 0, Producer, NULL, 0, NULL);
_beginthreadex(NULL, 0, Consumer, NULL, 0, NULL);
Sleep(3000);
CloseHandle(hMutex);
CloseHandle(hProduceEvent);
CloseHandle(hConsumeEvent);
return 0;
}