Creating Threads Program Example
The CreateThread() function creates a new thread for a process. The creating thread must specify the starting address of the code that the new thread is to execute. Typically, the starting address is the name of a function defined in the program code. This function takes a single parameter and returns a DWORD value. A process can have multiple threads simultaneously executing the same function. The following is a simple example that demonstrates how to create a new thread that executes the locally defined function, MyThreadFunction(). The calling thread uses the WaitForMultipleObjects() function to persist until all worker threads have terminated. The calling thread blocks while it is waiting; to continue processing, a calling thread would use WaitForSingleObject() and wait for each worker thread to signal its wait object. Note that if you were to close the handle to a worker thread before it terminated, this does not terminate the worker thread. However, the handle will be unavailable for use in subsequent function calls.
Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.
Then, add the source file and give it a suitable name.
Next, add the following source code.
#include <windows.h>
#include <strsafe.h>
// Constants
#define MAX_THREADS 3
#define BUF_SIZE 255
// Prototypes
DWORD WINAPI MyThreadFunction(LPVOID lpParam);
void ErrorHandler(LPTSTR lpszFunction);
// Sample custom data structure for threads to use.
// This is passed by void pointer so it can be any data type
// that can be passed using a single void pointer (LPVOID).
typedef struct MyData {
int val1;
int val2;
} MYDATA, *PMYDATA;
// This should be the parent process
int wmain(int argc, WCHAR **argv)
{
PMYDATA pDataArray[MAX_THREADS];
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
DWORD Ret = 0;
wprintf(LParent process ID: %u\n, GetCurrentProcessId());
wprintf(LParent thread ID: %u\n, GetCurrentThreadId());
// Create MAX_THREADS worker threads, in this case = 3
for(int i=0; i<MAX_THREADS; i++)
{
// Allocate memory for thread data
pDataArray[i] = (PMYDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYDATA));
if(pDataArray[i] == NULL)
{
// If the array allocation fails, the system is out of memory
// so there is no point in trying to print an error message.
// Just terminate execution.
wprintf(L\nHeapAlloc() failed!\n);
ExitProcess(2);
}
wprintf(L\nHeapAlloc() for thread #%u should be fine!\n, i);
// Generate unique data for each thread to work with
pDataArray[i]->val1 = i;
pDataArray[i]->val2 = i+100;
// Create the thread to begin execution on its own
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name - a pointer to the application-defined
// function to be executed by the thread
pDataArray[i], // argument to thread function
0, // use default creation flags
&dwThreadIdArray[i]); // returns the thread identifier
// Check the return value for success.
// If CreateThread() fails, terminate execution.
// This will automatically clean up threads and memory.
if (hThreadArray[i] == NULL)
{
ErrorHandler(LCreateThread());
ExitProcess(3);
}
wprintf(LCreateThread() for thread #%i is fine!\n, i);
wprintf(LCurrent process ID: %u\n, GetCurrentProcessId());
wprintf(LCurrent thread ID: %u\n, dwThreadIdArray[i]);
} // End of main thread creation loop.
// Wait until all threads have terminated
Ret = WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
wprintf(LWaitForMultipleObjects() return value is 0X%.8X\n, Ret);
// Close all thread handles and free memory allocations
wprintf(L\n);
for(int i=0; i<MAX_THREADS; i++)
{
wprintf(LClosing thread's handle #%i\n, i);
CloseHandle(hThreadArray[i]);
if(pDataArray[i] != NULL)
{
HeapFree(GetProcessHeap(), 0, pDataArray[i]);
pDataArray[i] = NULL; // Ensure address is not reused.
}
}
return 0;
}
// Thread creation function
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
HANDLE hStdout;
PMYDATA pDataArray;
WCHAR msgBuf[BUF_SIZE];
size_t cchStringSize;
DWORD dwChars;
// Make sure there is a console to receive output results
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if(hStdout == INVALID_HANDLE_VALUE)
return 1;
else
wprintf(LHandle to the standard output is OK!\n);
// Cast the parameter to the correct data type
// The pointer is known to be valid because
// it was checked for NULL before the thread was created
pDataArray = (PMYDATA)lpParam;
// Print the parameter values using thread-safe functions
StringCchPrintf(msgBuf, BUF_SIZE, LParameters = %d, %d\n, pDataArray->val1, pDataArray->val2);
StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);
return 0;
}
void ErrorHandler(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) lpszFunction) + 40) * sizeof(WCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(WCHAR), L%s failed with error %d: %s, lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR) lpDisplayBuf, LError, MB_OK);
// Free error-handling buffer allocations
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
Build and run the project. The following screenshot is a sample output.
From the sample output, you should notice something that was expected. We have the same process ID (3024) for all threads, which is the main or parent program (wmain()).We loop the thread creation three times, however there are 4 threads. The extra thread is the wmain()'s thread. The MyThreadFunction() function avoids the use of the C run-time library (CRT), as many of its functions are not thread-safe, particularly if you are not using the multithreaded CRT. If you would like to use the CRT in a ThreadProc() function, use the _beginthreadex() function instead. It is risky to pass the address of a local variable if the creating thread exits before the new thread, because the pointer becomes invalid. Instead, either pass a pointer to dynamically allocated memory or make the creating thread wait for the new thread to terminate. Data can also be passed from the creating thread to the new thread using global variables. With global variables, it is usually necessary to synchronize access by multiple threads. The creating thread can use the arguments to CreateThread() to specify the following:
You can also create a thread by calling the CreateRemoteThread() function. This function is used by debugger processes to create a thread that runs in the address space of the process being debugged.