A thread terminates when the thread function exits. The return value from the thread function is the thread's exit code. The exit code can be used to communicate success or failure to other threads. A thread function, or any function it calls, can terminate prematurely by calling ExitThread. This function is passed a single DWORD parameter that is used as the thread's exit code: ExitThread(10); // set thread exit code to 10 The code in Listing 5.8 creates a thread, and then calls WaitForSingleObject to block the primary thread until this thread terminates, or until 5000 milliseconds have elapsed. It is a good idea to set a timeout on waiting for a thread just in case the thread function fails. The thread function calls ExitThread to prematurely terminate the thread and set the exit code to 10. Note that the output message and the return statement will never be executed. Once WaitForSingleObject unblocks the function, GetExitCodeThread is called to retrieve the exit code (which should be 10). This function takes the thread handle and a pointer to a DWORD to receive the exit code. Listing 5.8 Thread exit codes DWORD WINAPI MyThreadProc2(LPVOID lpParameter) { ExitThread(10); cout _T("This message is not displayed") endl; return 0; } void Listing5_8() { HANDLE hThread; DWORD dwExitCode; hThread = CreateThread(NULL, 0, MyThreadProc2, NULL, 0, NULL); if(hThread == NULL) cout _T("Could not create thread") endl; else { if(WaitForSingleObject(hThread, 5000) == WAIT_FAILED) cout _T("Could not wait on thread") endl; GetExitCodeThread(hThread, &dwExitCode); CloseHandle(hThread); cout _T("Thread Exit code: ") dwExitCode endl; } } A thread can be terminated by another thread through calling TerminateThread. This function is passed the kernel object handle of the thread to terminate, and a DWORD specifying the exit code to set for the thread. DWORD dwExitCode = 20; if(!TerminateThread(hThread, dwExitCode)) cout _T("Could not terminate thread.") endl; As with TerminateProcess, you should only use TerminateThread as a last resort. Dynamic Link Libraries may have allocated thread local storage (TLS, described later in the chapter), and the DLLs will not have the opportunity to free this resource. Thread StatesA thread can exist in one of the following states:
The functions SuspendThread and ResumeThread are used to change a thread from running to suspended, and vice versa. A thread can suspend itself but cannot resume itself since it is not executing, and so cannot call ResumeThread. In Windows CE versions prior to 3.0, the minimum time a thread could be put to sleep was around 25 milliseconds. In version 3.0 the Sleep function can sleep a thread for a period of 1 millisecond or greater. This is due to changes to thread scheduling described later in this chapter. Further, the GetTickCount function (which returns the number of milliseconds elapsed since the device was powered-on) provides a resolution down to a millisecond. In Listing 5.9 the primary thread is put to sleep for a single millisecond, and the amount of time spent sleeping is recorded. In Windows CE 3.0, the program will record that the thread was asleep for around two milliseconds (the sleep time, plus overhead of calling GetTickCount), whereas in Windows CE versions prior to 3.0, a value of around 25 milliseconds or more will be recorded. Listing 5.9 Sleeps a thread void Listing5_9() { DWORD dwTickCount = GetTickCount(); Sleep(1); dwTickCount = GetTickCount() dwTickCount; cout _T("Sleep for: ") dwTickCount endl; } You can pass the value "0" to the Sleep function, and this yields control back to the thread scheduler regardless of whether the thread's time quantum was up. This can be used to allow other threads with the same priority an opportunity to execute immediately. This can be useful when synchronizing threads.
|