Thread Management
It should come as no surprise that threads, like any other Windows object, have handles and that there is a CreateThread system call to create an executable thread in the calling process's address space. As with processes, we will sometimes speak of "parent" and "child" threads, although the OS does not make any such distinction. CreateThread has several unique requirements.Chapter 5). New stack pages are committed as required until the stack reaches its maximum size and cannot grow anymore.
HANDLE CreateThread (
LPSECURITY_ATTRIBUTES lpsa,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddr,
LPVOID lpThreadParm,
DWORD dwCreationFlags,
LPDWORD lpThreadId)
Parameters
lpsa is the familiar security attributes structure.dwStackSize is the byte size of the new thread's stack. Use 0 to default to the primary thread's stack size.lpStartAddr points to the function (within the calling process) to be executed. This function accepts a single pointer argument and returns a 32-bit DWORD exit code. The thread can interpret the argument as a DWORD or a pointer. The thread function signature, then, is as follows:
DWORD WINAPI ThreadFunc (LPVOID)
lpThreadParm is the pointer passed as the thread argument and is interpreted by the thread, normally as a pointer to an argument structure.Chapter 4) will be called with DLL_THREAD_DETACH as the "reason."
VOID ExitThread (DWORD dwExitCode)
When the last thread in a process terminates, the process itself terminates.One thread can terminate another thread with the TerminateThread function, but the thread's resources will not be deallocated, completion handlers will not be executed, and attached DLLs will not be notified. It is best if the thread terminates itself; TerminateThread usage is strongly discouraged. TerminateThread has the same disadvantages as those of TerminateProcess.A terminated thread (again, a thread normally should terminate itself) will continue to exist until the last handle to it is closed using CloseHandle. Any other thread, perhaps one waiting for some other thread to terminate, can retrieve the exit code.
BOOL GetExitCodeThread (
HANDLE hThread,
LPDWORD lpExitCode)
lpExitCode will contain the thread's exit code. If the thread is still running, the value is STILL_ACTIVE.
Thread Identity
You can obtain thread IDs and handles using functions that are similar to those used with processes.
- GetCurrentThread returns a noninheritable pseudohandle to the calling thread.Program 6-3), and OpenThread can be used in a similar fashion.
Additional Thread Management Functions
While the thread management functions discussed above are sufficient in most cases, including the examples in this book, two additional functions were introduced in XP and Windows 2003. Brief descriptions follow.
- GetProcessIdOfThread, which requires Windows 2003, finds the process ID of a thread from the thread's handle. You could use this function in a program that manages or interacts with threads in other processes. If necessary, use OpenProcess to obtain a process handle.
- GetTHReadIOPendingFlag determines whether the thread, identified by its handle, has any outstanding I/O requests. For example, the thread might be blocked on a ReadFile operation. The result is the status at the time that the function is executed; the actual status could change at any time if the target thread completes or initiates an operation. This function requires NT 5.1 and is therefore available only in XP and Windows 2003.
Suspending and Resuming Threads
Every thread has a suspend count, and a thread can execute only if this count is 0. One thread can increment or decrement the suspend count of another thread using SuspendThread and ResumeThread. Recall that a thread can be created in the suspended state with a count of 1.
DWORD ResumeThread (HANDLE hThread)
DWORD SuspendThread (HANDLE hThread)
Both functions, if successful, return the previous suspend count. 0xFFFFFFFF indicates failure.
Waiting for Threads to Terminate
One thread can wait for another thread to terminate in the same way that threads wait for process termination, as discussed in Chapter 6. Use a wait function (WaitForSingleObject or WaitForMultipleObjects) using thread handles instead of process handles. Note that the handles in the array passed to WaitForMultipleObjects do not all need to be of the same type; for example, thread, process, and other handle types can be mixed in a single call.Program 6-1 already illustrated this technique; the programs in this book will perform single waits, but the full solution is on the book's Web site.The wait function waits for the object, indicated by the handle, to become signaled. In the case of threads, ExitThread and TerminateThread set the object to the signaled state, releasing all other threads waiting on the object, including threads that might wait in the future after the thread terminates. Once a thread handle is signaled it never becomes nonsignaled. The same is true of process handles but not of handles to some other objects, such as mutexes and events (described in the next chapter).Note that multiple threads can wait on the same object. Similarly, the ExitProcess function sets the process state and the states of all its threads to signaled.
Remote Threads
The CreateRemoteThread function allows creation of a thread in another process. Compared with CreateThread, there is an additional parameter for the process handle, and the function addresses must be in the target process's address space. CreateRemoteThread is one of several interesting, and potentially dangerous, ways for one process to affect another directly, and it might be useful in writing, for example, a debugger.Chapter 10, however, shows a much safer method for one thread to cancel another, using asynchronous procedure calls.
Threads are a well-established concept in many OSs, and historically, many UNIX vendors and users have provided their own proprietary implementations. Some thread libraries have been implemented outside the kernel. POSIX Pthreads are now the standard. Pthreads are included as part of commercial UNIX and Linux implementations and are sometimes considered to be a part of UNIX. The system calls are distinguished from normal UNIX system calls by the pthread_ prefix name. Pthreads are also supported on some proprietary non-UNIX systems such as OpenVMS.Chapter 10 will show a method to achieve the same effect. |