Win32 APIs for Threads

The Win32 API to create a new thread is

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to security attributes
  DWORD dwStackSize,                         // initial thread stack size
  LPTHREAD_START_ROUTINE lpStartAddress,     // pointer to thread function
  LPVOID lpParameter,                        // argument for new thread
  DWORD dwCreationFlags,                     // creation flags
  LPDWORD lpThreadId                         // pointer to receive thread ID
);

Although this takes a few more arguments than pthread_create it works in a very similar way. If the first argument lpThreadAttributes is set to NULL, and the second argument dwStackSize is set to zero, appropriate default values will be assigned. The third argument lpStartAddress should be set to the name of the function to be called. The fourth argument lpParameter is a pointer to the argument to be passed to the function. The fifth argument dwCreationFlags should be set to zero. The last argument is a pointer to a DWORD. This will be set by the function.

The function returns a HANDLE value if successul. If it fails, the return value will be NULL.

The called function must have the following signature:
DWORD WINAPI ThreadProc(LPVOID lpParameter); replacing ThreadProc with the name of the function.

The Win32 equivalent of pthread_join is

DWORD WaitForSingleObject(
  HANDLE hHandle,        // handle to object to wait for
  DWORD dwMilliseconds   // time-out interval in milliseconds
);
This is a very important function because it is used to wait for all kinds of different events, and we will see it a lot in this course. In this case it is used to wait for a thread to terminate. The first argument hHandle is the handle returned from CreateThread. Like pthread_join, this function blocks until the thread terminates. However, unlike pthread_join this function allows you to specify how long you are willing to wait for the event before becoming unblocked. The second argument is the number of milliseconds to wait. if this value is zero, the function returns immediately, even if the thread has not terminated. Another possible value is the keyword INFINITE, which causes the function to block indefinitely if the thread does not terminate.

Here is why this may be useful. Supposed there is an infinite loop or other code in the thread function which means that the thread will never terminate. If the second argument of WaitForSingleObject was set to INFINITE, this would mean that the main thread would be indefinitely blocked. On the other hand, if the second argument was set to, say, 1000, then it would wake up after a one second interval.

Here is a simple sample program.

#include <windows.h>
#include <stdio.h>

DWORD WINAPI ThreadRoutine(LPVOID lpArg)
{
    int a;
	a = *(int *)lpArg;
	fprintf(stderr,"My argument is %d\n",a);
	return NULL;
}

int main()
{
	int i;
	int *lpArgPtr;
	HANDLE hHandles[5];
	DWORD ThreadId;
	
	for (i=0;i < 5;i++) {
		lpArgPtr = (int *)malloc(sizeof(int));
		*lpArgPtr = i;
		hHandles[i] = CreateThread(NULL,0,ThreadRoutine,lpArgPtr,0,&ThreadId);
		if (hHandles[i] == NULL) {
			fprintf(stderr,"Could not create Thread\n");
			exit(0);
		}
		else printf("Thread %d was created\n",ThreadId);
	}

	for (i=0;i < 5;i++) {
		WaitForSingleObject(hHandles[i],INFINITE);
	}
	return 0;
}

To terminate a particular thread without terminating the entire process, use the API
VOID ExitThread(DWORD ExitCode);
where ExitCode is a value to be returned to another thread.

A thread can read the ExitCode of another thread which has terminated with the API
BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpdwExitCode);
This should only be called if the thread has terminated, so it is usually used in combination with the API
WaitForSingleObject

Here are links to the on line help pages for these functions
CreateThread
WaitForSingleObject
ExitThread
GetExitCodeThread