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

Το παρακάτω πρόγραμμα, 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 = {0, 0};
	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(-3, 3);
	Delta.Y = getrandom(-3, 3);

	/* 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(400, 50);
		}
		if (Coords.Y < 0 || Coords.Y > csbiInfo.dwSize.Y) {
			Delta.Y = -Delta.Y;
			Beep(600, 50);
		}
	}
	/* 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 = {0, 0};
	FillConsoleOutputCharacter(hConsoleOut, ' ', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy);
}