Ejemplo de programa multiproceso en C
Actualización: noviembre 2007
Bounce.c es un ejemplo de programa multiproceso que crea un nuevo subproceso cada vez que se escribe la letra a o A. Cada subproceso produce una cara sonriente de diferente color que se mueve por la pantalla. Pueden crearse hasta 32 subprocesos. La terminación normal del programa se produce al presionar la tecla q o Q. Para obtener información sobre la compilación y vinculación de Bounce.c, vea Compilar y vincular programas multiproceso.
Ejemplo
Código
// sample_multithread_c_program.c
// compile with: /c
//
// 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.
//
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <process.h>
#define MAX_THREADS 32
// The function getrandom returns a random number between
// min and max, which must be in integer range.
#define getrandom( min, max ) (SHORT)((rand() % (int)(((max) + 1) - \
(min))) + (min))
int main( void ); // Thread 1: main
void KbdFunc( void ); // Keyboard input, thread dispatch
void BounceProc( void * 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
int 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 );
}
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();
}
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();
}
void BounceProc( void *pMyID )
{
char MyCell, OldCell;
WORD MyAttrib, OldAttrib;
char BlankCell = 0x20;
COORD Coords, Delta;
COORD Old = {0,0};
DWORD Dummy;
char *MyID = (char*)pMyID;
// Generate update increments and initial
// display coordinates.
srand( (unsigned int) *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 )
{
enum {
sizeOfNThreadMsg = 80
};
char NThreadMsg[sizeOfNThreadMsg];
sprintf_s( NThreadMsg, sizeOfNThreadMsg,
"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 );
}
Entrada
a
q