A thread can be terminated in four ways:
This section discusses all four methods for terminating a thread and describes what happens when a thread ends.
You should always design your thread functions so that they return when you want the thread to terminate. This is the only way to guarantee that all your thread's resources are cleaned up properly.
Having your thread function return ensures the following:
You can force your thread to terminate by having it call ExitThread:
VOID ExitThread(DWORD dwExitCode); |
This function terminates the thread and causes the operating system to clean up all of the operating system resources that were used by the thread. However, your C/C++ resources (such as C++ class objects) will not be destroyed. For this reason, it is much better to simply return from your thread function instead of calling ExitThread yourself. (For more information, see the section titled "The ExitProcess Function" in Chapter 4.)
Of course, you use ExitThread's dwExitCode parameter to tell the system what to set the thread's exit code to. The ExitThread function does not return a value because the thread has terminated and cannot execute any more code.
NOTE
The recommended way to have a thread terminate is by having its thread function simply return (as described in the previous section). However, if you use the method described in this section, be aware that the ExitThread function is the Windows function that kills a thread. If you are writing C/C++ code, you should never call ExitThread. Instead, you should use the Visual C++ run-time library function _endthreadex. If you do not use Microsoft's Visual C++ compiler, your compiler vendor will have its own alternative to ExitThread. Whatever this alternative is, you must use it. I will explain what _endthreadex does and why it is so important later in this chapter.
A call to TerminateThread also kills a thread:
BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode); |
Unlike ExitThread, which always kills the calling thread, TerminateThread can kill any thread. The hThread parameter identifies the handle of the thread to be terminated. When the thread terminates, its exit code becomes the value you passed as the dwExitCode parameter. Also, the thread's kernel object has its usage count decremented.
NOTE
The TerminateThread function is asynchronous. That is, it tells the system that you want the thread to terminate but the thread is not guaranteed to be killed by the time the function returns. If you need to know for sure that the thread has terminated, you might want to call WaitForSingleObject (described in Chapter 9) or a similar function, passing the handle of the thread.
A well-designed application never uses this function because the thread being terminated receives no notification that it is dying. The thread cannot clean up properly and it cannot prevent itself from being killed.
NOTE
When a thread dies by returning or calling ExitThread, the stack for the thread is destroyed. However, if TerminateThread is used, the system does not destroy the thread's stack until the process that owned the thread terminates. Microsoft purposely implemented TerminateThread in this way. If other still-executing threads were to reference values on the forcibly killed thread's stack, these other threads would raise access violations. By leaving the killed thread's stack in memory, other threads can continue to execute just fine.In addition, DLLs usually receive notifications when a thread is terminating. If a thread is forcibly killed with TerminateThread, however, the DLLs do not receive this notification, which can prevent proper cleanup. (See Chapter 20 for more information.)
The ExitProcess and TerminateProcess functions discussed in Chapter 4 also terminate threads. The difference is that these functions terminate all the threads contained in the process being terminated. Also, since the entire process is being shut down, all resources in use by the process are guaranteed to be cleaned up. This certainly includes any and all thread stacks. These two functions cause the remaining threads in the process to be forcibly killed, as if TerminateThread were called for each remaining thread. Obviously, this means that proper application cleanup does not occur: C++ object destructors aren't called, data isn't flushed to disk, and so on.
The following actions occur when a thread terminates:
When a thread terminates, its associated thread kernel object doesn't automatically become freed until all the outstanding references to the object are closed.
Once a thread is no longer running, there isn't much any other thread in the system can do with the thread's handle. However, these other threads can call GetExitCodeThread to check whether the thread identified by hThread has terminated and, if it has, determine its exit code:
BOOL GetExitCodeThread( HANDLE hThread, PDWORD pdwExitCode); |
The exit code value is returned in the DWORD pointed to by pdwExitCode. If the thread hasn't terminated when GetExitCodeThread is called, the function fills the DWORD with the STILL_ACTIVE identifier (defined as 0x103). If the function is successful, TRUE is returned. (Chapter 9 has more on using the thread's handle to determine when the thread has terminated.)