A process begins with one initial thread. The initial thread can create new threads, which in turn can create their own new threads. Under normal conditions, the process ends when each thread in the process terminates.
Creating a thread begins with the CreateThread function call.
We can instruct the parent thread to wait for the child thread by using the WaitForSingleObject function.
lpThreadAttributes can be set to NULL to use the default security attributes. dwStackSize can be set to the default size by passing in 0. The default is 1 MB reserved from the paging file. The lpStartAddress, the third parameter, should be the identifier of the function we wish to run in the new thread. The lpParameter provides a way to pass parameters into the new thread. The lpParameter is of type LPVOID, which is defined as typedef void*. A void pointer can hold the address to data of any type. The dwCreationFlags parameter allows us to create a suspended thread. The default is for the thread to start immediately. The lpThreadId parameter holds a pointer to a DWORD value. The thread ID of the new thread will be stored at this memory location.
The threads in a process have their priority set relative to their parent process. The priority of threads determines how much CPU time they get relative to one another and to other threads in other processes. We can set a thread’s priority using the SetThreadPriority function, and we can get a thread’s priority using the GetThreadPriority function. Threads initially start at THREAD_PRIORITY_NORMAL.
The CloseHandle Function
The CloseHandle function when called with a thread decrements the reference count of the thread kernel object. When the count reaches zero, the object itself is freed. The default reference count for a thread object is two, sibce the thread itself has a reference to the kernel object, and the HANDLE returned from the CreateThread function does as well. When we call the CloseHandle function the reference count drops, and when the thread itself terminats the count also drops. Both are required for the thread kernel object to be destroyed.
The CreateThread function returns a HANDLE that is local to the process. This handle refers to a kernel object that is managed by KERNEL32.DLL. The handel points to something that our program is not allowed to access directly, for security and system integrity reasons.
We can use the GetExitCodeThread function to get the status of a thread. We supply the HANDLE to the kernel object and a pointer to a DWORD variable to store the status.
The GetExitCodeThread function returns a Boolean value indicating whether or not it succeeded. If the thread is still active, the value stored in the DWORD variable will be STILL_ACTIVE.
We can suspend and resume threads using the SuspendThread and ResumeThread functions.
Note that if we start a thread in a suspended state by setting the dwCreationFlags parameter to CREATE_SUSPENDED.
The priority of threads determines the amount of CPU time they are allocated relative to one another and to other threads in other processes. We can set the priority of a thread using the SetThreadPriority function and retrieve the priority using GetThreadPriority function.