Παράδειγμα εφαρμογής

Το παρακάτω πρόγραμμα, bounce.c (βασισμένο σε πρόγραμμα που περιέχεται ως παράδειγμα στην τεκμηρίωση της Microsoft Visual C++) δημιουργεί ένα νέο νήμα που κινεί ένα πρόσωπο στην οθόνη κάθε φορά που πληκτρολογούμε A ή a. Τερματίζει τη λειτουργία του όταν πληκτρολογήσουμε q ή Q.
/*
 * Bounce - Creates a new thread each time the letter 'a' is typed. Each
 * thread bounces a happy face of a different color around the screen. All
 * threads are terminated when the letter 'Q' is entered. 
 *
 * This program requires the multithread library. For example, compile with the
 * following command line: CL /MT BOUNCE.C 
 */

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <process.h>

const int MAX_THREADS=32;

/*
 * getrandom returns a random number between min and max, which must be in
 * integer range. 
 */
static int
getrandom(int min, int max)
{
        return ((rand() % (int)(((max) + 1) - (min))) + (min));
}

void main(void);                        /* Thread 1: main */
void KbdFunc(void);                     /* Keyboard input, thread dispatch */
void BounceProc(char *MyID);            /* Threads 2 to n: display */
void ClearScreen(void);                 /* Screen clear */
void ShutDown(void);                    /* Program shutdown */
void WriteTitle(int ThreadNum);         /* Display title bar information */

HANDLE          hConsoleOut;    /* Handle to the console */
HANDLE          hRunMutex;      /* "Keep Running" mutex */
HANDLE          hScreenMutex;   /* "Screen update" mutex  */
int             ThreadNr;       /* Number of threads started */
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;    /* Console information */


void 
main()
{                               /* Thread One */
        /* Get display screen information & clear the screen. */
        hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
        GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo);
        ClearScreen();
        WriteTitle(0);
        /* Create the mutexes and reset thread count. */
        hScreenMutex = CreateMutex(NULL, FALSE, NULL);  /* Cleared */
        hRunMutex = CreateMutex(NULL, TRUE, NULL);      /* Set */
        ThreadNr = 0;

        /* Start waiting for keyboard input to dispatch threads or exit. */
        KbdFunc();

        /* All threads done. Clean up handles. */
        CloseHandle(hScreenMutex);
        CloseHandle(hRunMutex);
        CloseHandle(hConsoleOut);
}

/*
 * Finish processing
 */
void 
ShutDown(void)
{                               /* Shut down threads */
        while (ThreadNr > 0) {
                /* Tell thread to die and record its death. */
                ReleaseMutex(hRunMutex);
                ThreadNr--;
        }
        /* Clean up display when done */
        WaitForSingleObject(hScreenMutex, INFINITE);
        ClearScreen();
}

/*
 * Read an process keyboard commands
 */
void 
KbdFunc(void)
{                               /* Dispatch and count threads. */
        int             KeyInfo;

        do {
                KeyInfo = _getch();
                if (tolower(KeyInfo) == 'a' && ThreadNr < MAX_THREADS) {
                        ThreadNr++;
                        _beginthread(BounceProc, 0, &ThreadNr);
                        WriteTitle(ThreadNr);
                }
        } while (tolower(KeyInfo) != 'q');

        ShutDown();
}

/*
 * Bounce the face around the screen.
 * This procedure is run by each thread.
 */
void 
BounceProc(char *MyID)
{
        char            MyCell, OldCell;
        WORD            MyAttrib, OldAttrib;
        char            BlankCell = 0x20;
        COORD           Coords, Delta;
        COORD           Old = {00};
        DWORD           Dummy;

        /* Generate update increments and initial display coordinates. */
        srand((unsigned) *MyID * 3);
        Coords.X = getrandom(0, csbiInfo.dwSize.X - 1);
        Coords.Y = getrandom(0, csbiInfo.dwSize.Y - 1);
        Delta.X = getrandom(-33);
        Delta.Y = getrandom(-33);

        /* Set up "happy face" & generate color attribute from thread number. */
        if (*MyID > 16)
                MyCell = 0x01;  /* outline face */
        else
                MyCell = 0x02;  /* solid face */
        MyAttrib = *MyID & 0x0F;/* force black background */

        do {
                /* Wait for display to be available, then lock it. */
                WaitForSingleObject(hScreenMutex, INFINITE);

                /* If we still occupy the old screen position, blank it out. */
                ReadConsoleOutputCharacter(hConsoleOut, &OldCell, 1, Old, &Dummy);
                ReadConsoleOutputAttribute(hConsoleOut, &OldAttrib, 1, Old, &Dummy);
                if ((OldCell == MyCell) && (OldAttrib == MyAttrib))
                        WriteConsoleOutputCharacter(hConsoleOut, &BlankCell, 1, Old, &Dummy);

                /* Draw new face, then clear screen lock */
                WriteConsoleOutputCharacter(hConsoleOut, &MyCell, 1, Coords, &Dummy);
                WriteConsoleOutputAttribute(hConsoleOut, &MyAttrib, 1, Coords, &Dummy);
                ReleaseMutex(hScreenMutex);

                /* Increment the coordinates for next placement of the block. */
                Old.X = Coords.X;
                Old.Y = Coords.Y;
                Coords.X += Delta.X;
                Coords.Y += Delta.Y;

                /* If we are about to go off the screen, reverse direction */
                if (Coords.X < 0 || Coords.X >= csbiInfo.dwSize.X) {
                        Delta.X = -Delta.X;
                        Beep(40050);
                }
                if (Coords.Y < 0 || Coords.Y > csbiInfo.dwSize.Y) {
                        Delta.Y = -Delta.Y;
                        Beep(60050);
                }
        }
        /* Repeat while RunMutex is still taken. */
        while (WaitForSingleObject(hRunMutex, 75L) == WAIT_TIMEOUT);

}

void 
WriteTitle(int ThreadNum)
{
        char            NThreadMsg[80];

        sprintf(NThreadMsg, "Threads running: %02d.  Press 'A' to start a thread,'Q' to quit.", ThreadNum);
        SetConsoleTitle(NThreadMsg);
}

void 
ClearScreen(void)
{
        DWORD           dummy;
        COORD           Home = {00};
        FillConsoleOutputCharacter(hConsoleOut, ' ', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy);
}