Terminating a Thread

[Previous] [Next]

A thread can be terminated in four ways:

  • The thread function returns. (This is highly recommended.)
  • The thread kills itself by calling the ExitThread function. (Avoid this method.)
  • A thread in the same or in another process calls the TerminateThread function. (Avoid this method.)
  • The process containing the thread terminates. (Avoid this method.)

This section discusses all four methods for terminating a thread and describes what happens when a thread ends.

The Thread Function Returns

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:

  • Any and all C++ objects created in your thread function will be destroyed properly via their destructors.
  • The operating system will properly free the memory used by the thread's stack.
  • The system will set the thread's exit code (maintained in the thread's kernel object) to your thread function's return value.
  • The system will decrement the usage count of the thread's kernel object.

The ExitThread Function

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.

The TerminateThread Function

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.)

When a Process Terminates

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.

When a Thread Terminates

The following actions occur when a thread terminates:

  • All User object handles owned by the thread are freed. In Windows, most objects are owned by the process containing the thread that creates the objects. However, a thread owns two User objects: windows and hooks. When a thread dies, the system automatically destroys any windows and uninstalls any hooks that were created or installed by the thread. Other objects are destroyed only when the owning process terminates.
  • The thread's exit code changes from STILL_ACTIVE to the code passed to ExitThread or TerminateThread.
  • The state of the thread kernel object becomes signaled.
  • If the thread is the last active thread in the process, the system considers the process terminated as well.
  • The thread kernel object's usage count is decremented by 1.

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.)



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net