Tricks of the Windows Game Programming Gurus:Algorithms, Data Structures, Memory Management, and Multithreading

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

#### Sending Messages from Thread to Thread

Let’s say that you want the primary thread to have control over the spawned threads that it creates. For example, the primary thread may want to kill all the secondary threads. How can you do this? Well, there are a couple of methods to terminate a thread:

  • Sending a message to the thread to tell it to terminate itself (the right way).
  • Simply making a kernel-level call and killing the thread (the wrong way).

Although the wrong way might be needed in some cases, it is not safe because it simply pulls the carpet right from under the thread. If the thread needs to perform any clean-up, it never will. This can create memory and resource leaks, so be careful. Figure 11.20 illustrates the different methods to instruct a thread to terminate.

Figure 11.20 Thread termination methods.

Before you see an example of sending messages to the threads to notify them that they should terminate, take a look at the `TerminateThread()` function call so you know how to use it if the need arises:

    BOOL TerminateThread(HANDLE  hThread,    // handle to the thread

                  DWORD  dwExitCode);    // exit code for the thread

`hThread` identifies the thread to terminate. The handle must have `THREAD_TERMINATE` access.

`dwExitCode` specifies the exit code for the thread. Use the `GetExitCodeThread()` function to retrieve a thread’s exit value.

If the function succeeds, the return value is `TRUE`. If the function fails, the return value is `FALSE`. To get extended error information, call `GetLastError()`.

`TerminateThread()` is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code and its initial stack is not deallocated. DLLs attached to the thread are not notified that the thread is terminating, and that’s a bad thing. :)

To use `TerminateThread()`, simply call it with the handle to the thread you want to terminate, along with a return code override, and it will be history. Now, don’t get me wrong; the function wouldn’t exist if there wasn’t a use for it. Just make sure that you know what you’re doing when you use it and that you’ve thought of everything.

Let’s move on to the message-passing method of terminating a thread. It works by setting a global variable that the secondary threads watch. Then, when the secondary threads see that the global termination flag has been set, they all terminate. But how does the primary thread know when all the secondary threads have terminated? Well, one way to accomplish the task is have another global variable that the threads decrement when they terminate—a reference counter of sorts.

This counter can be tested by the primary thread, and when it’s equal to 0, all the secondary threads have terminated and the primary thread can be confident that it’s okay to proceed with work and close the handles to the threads. This is almost true… We’ll get to the “almost” part after you see a full example of this new message passing system. `DEMO11_7.CPP[vbar]EXE` illustrates global message passing and is shown here:

    // DEMO11_7.CPP - An example of global message passing to control

    // termination of threads.

    // INCLUDES   ///////////////////////////////////////////////////////

    #define WIN32_LEAN_AND_MEAN  // make sure certain headers

                                 // are included correctly

    #include <windows.h>         // include the standard windows stuff

    #include <windowsx.h>        // include the 32 bit stuff

    #include <conio.h>

    #include <stdlib.h>

    #include <stdarg.h>

    #include <stdio.h>

    #include <math.h>

    #include <io.h>

    #include <fcntl.h>

    // DEFINES //////////////////////////////////////////////////////

    #define MAX_THREADS 3

    // PROTOTYPES //////////////////////////////////////////////////

    DWORD WINAPI Printer_Thread(LPVOID data);

    // GLOBALS /////////////////////////////////////////////////////

    int terminate_threads = 0;  // global message flag to terminate

    int active_threads    = 0;  // number of active threads

    // FUNCTIONS ////////////////////////////////////////////////

    DWORD WINAPI Printer_Thread(LPVOID data)

    {

    // this thread function simply prints out data until it is told to

    terminate

    for(;;)

        {

        printf("%d ",(int)data+1); // output a single character

       Sleep(100);                     // sleep a little to slow things down

                                      // test for termination message

        if (terminate_threads)

            break;

        } // end for index

    // decrement number of active threads

    if (active_threads > 0)

       active_threads--;

    // just return the data sent to the thread function

    return((DWORD)data);

    } // end Printer_Thread

    // MAIN //////////////////////////////////////////////////////

    void main(void)

    {

    HANDLE thread_handle[MAX_THREADS];  // this holds the

                                        // handles to the threads

    DWORD  thread_id[MAX_THREADS];      // this holds the ids of the threads

    // start with a blank line

    printf("\nStarting Threads…\n");

    // create the thread, IRL we would check for errors

    for (int index=0; index < MAX_THREADS; index++)

        {

        thread_handle[index] = CreateThread(NULL, // default security

                   0,            // default stack

                Printer_Thread,    // use this thread function

                 (LPVOID)index,     // user data sent to thread

                0,            // creation flags, 0=start now.

                &thread_id[index]);// send id back in this var

        // increment number of active threads

        active_threads++;

        } // end for index

    // now enter into printing loop, make sure this

    // takes longer than threads,

    // so threads finish first, note that primary thread prints 4

    for (index=0; index<25; index++)

        {

        printf("4 ");

        Sleep(100);

        } // end for index

    // at this point all the threads are still running,

    // now if the keyboard is hit

    // then a message will be sent to terminate all the

    // threads and this thread

    // will wait for all of the threads to message in

    while(!kbhit());

    // get that char

    getch();

    // set global termination flag

    terminate_threads = 1;

    // wait for all threads to terminate,

    // when all are terminated active_threads==0

    while(active_threads);

    // at this point the threads should all be dead, so close handles

    for (index=0; index < MAX_THREADS; index++)

        CloseHandle(thread_handle[index]);

    // end with a blank line

    printf("\nAll threads terminated.\n");

    } // end main

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.