Παράδειγμα εφαρμογής
Το παρακάτω πρόγραμμα, 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);
}