Compartir a través de


Ejemplo de programa multiproceso en C

Bounce.c es un programa multiproceso de ejemplo que crea un subproceso cada vez que se escribe la letra a o A. Cada subproceso hace rebotar una letra de un color diferente por la pantalla. Se pueden crear hasta 32 subprocesos. La finalización normal del programa se produce cuando se escribe q o Q.

Los programas se compilan como multiproceso de forma predeterminada.

  1. En el menú Archivo, elija Nuevo>Proyecto.

  2. En el diálogo Crear un proyecto nuevo, selecciona la plantilla de Aplicación de consola que tenga las etiquetas C++, Windows y Consola. Elija Siguiente para continuar.

  3. En el diálogo Configure su nuevo proyecto, escribe un nombre para el proyecto, como "Devolución". Elige Crear para continuar.

  4. En la ventana Explorador de soluciones, abre la carpeta Archivos de código fuente en el proyecto y cambia el nombre del archivo de origen para que tenga una extensión .c.

  5. En la ventana de edición, elimina el código fuente existente y reemplázalo por el código de ejemplo.

  6. En el menú Compilar , elija Compilar solución.

  7. Presiona F5 para iniciar el programa en el depurador.

  1. En el menú Archivo, elija Nuevo>Proyecto.

  2. En el diálogo Nuevo proyecto, selecciona Visual C++ en el panel izquierdo y, a continuación, selecciona Proyecto vacío en el panel central.

  3. En el cuadro de edición Nombre, escribe un nombre para el proyecto, por ejemplo, "Devolución". Selecciona Aceptar para crear el proyecto vacío.

  4. En la ventana Explorador de soluciones, abre la carpeta Archivos de código fuente en el proyecto y agrega el archivo que contiene el código fuente de C al proyecto.

  5. En el menú Compilar, compila el proyecto eligiendo el comando Compilar solución.

  6. Presiona F5 para iniciar el programa en el depurador.

  7. Presiona un para crear un subproceso. Cada subproceso hace rebotar un carácter de un color diferente por la pantalla.

  8. Presione q para salir.

  1. Abra un símbolo del sistema de herramientas de Visual Studio. Esto garantiza que la ruta de acceso esté establecida para incluir el compilador.

  2. Compila y vincula el programa:

    cl bounce.c
    

Ejemplo

Para compilar en la línea de comandos, copia y guarda este ejemplo en un archivo de código fuente con una extensión .c. En el IDE, reemplace cualquier código fuente creado por la plantilla por este ejemplo:

// sample_multithread_c_program.c
// compile with: /c
//
//  Bounce - Creates a new thread each time the letter 'a' is typed.
//  Each thread bounces a character 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* pMyID);      // 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 = 0;                // Number of threads started
CONSOLE_SCREEN_BUFFER_INFO csbiInfo; // Console information
COORD   consoleSize;
BOOL    bTrails = FALSE;

HANDLE  hThreads[MAX_THREADS] = { NULL }; // Handles for created threads

int main(void) // Thread One
{
    // Get display screen information & clear the screen.
    hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hConsoleOut, &csbiInfo);
    consoleSize.X = csbiInfo.srWindow.Right;
    consoleSize.Y = csbiInfo.srWindow.Bottom;
    ClearScreen();
    WriteTitle(0);

    // Create the mutexes and reset thread count.
    hScreenMutex = CreateMutexW(NULL, FALSE, NULL);  // Cleared
    hRunMutex = CreateMutexW(NULL, TRUE, NULL);      // Set

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

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

void ShutDown(void) // Shut down threads
{
    // Tell all threads to die
    ReleaseMutex(hRunMutex);

    while (ThreadNr > 0)
    {
        // Wait for each thread to complete
        WaitForSingleObject(hThreads[--ThreadNr], INFINITE);
    }

    // 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;
            hThreads[ThreadNr] = 
                (HANDLE)_beginthread(BounceProc, 0, (void*)(uintptr_t)ThreadNr);
            WriteTitle(ThreadNr);
        }

        if (tolower(KeyInfo) == 't')
        {
            bTrails = !bTrails;
        }
    } while (tolower(KeyInfo) != 'q');

    ShutDown();
}

void BounceProc(void* pMyID)
{
    wchar_t MyCell, OldCell;
    WORD    MyAttrib, OldAttrib = 0;
    wchar_t BlankCell = 0x20;
    COORD   Coords, Delta;
    COORD   Old = { 0,0 };
    DWORD   Dummy;
    int MyID = (int)(uintptr_t)pMyID;

    // Generate update increments and initial
    // display coordinates.
    srand(MyID * 3);

    Coords.X = getrandom(0, consoleSize.X - 1);
    Coords.Y = getrandom(0, consoleSize.Y - 1);
    Delta.X = getrandom(-3, 3);
    Delta.Y = getrandom(-3, 3);

    // Set up character & generate color
    // attribute from thread number.
    if (MyID > 16)
        MyCell = (wchar_t)(0x60 + MyID - 16); // lower case
    else
        MyCell = (wchar_t)(0x40 + MyID);      // upper case
    MyAttrib = MyID & 0x0f;   // force black background

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

        if (!bTrails)
        {
            // If we still occupy the old screen position, blank it out.
            ReadConsoleOutputCharacterW(hConsoleOut, &OldCell, 1,
                Old, &Dummy);
            ReadConsoleOutputAttribute(hConsoleOut, &OldAttrib, 1,
                Old, &Dummy);
            if ((OldCell == MyCell) && (OldAttrib == MyAttrib))
                WriteConsoleOutputCharacterW(hConsoleOut, &BlankCell, 1,
                    Old, &Dummy);
        }

        // Draw new character, then clear screen lock
        WriteConsoleOutputCharacterW(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 >= consoleSize.X)
        {
            Delta.X = -Delta.X;
            Beep(400, 50);
        }
        if (Coords.Y < 0 || Coords.Y > consoleSize.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 = 120
    };
    wchar_t    NThreadMsg[sizeOfNThreadMsg] = { L"" };

    swprintf_s(NThreadMsg, sizeOfNThreadMsg,
        L"Threads running: %02d.  Press 'A' "
        L"to start a thread, 'T' to toggle "
        L"trails, 'Q' to quit.", ThreadNum);
    SetConsoleTitleW(NThreadMsg);
}

void ClearScreen(void)
{
    DWORD    dummy = 0;
    COORD    Home = { 0, 0 };
    FillConsoleOutputCharacterW(hConsoleOut, L' ',
        consoleSize.X * consoleSize.Y,
        Home, &dummy);
}

Consulte también

Multithreading con C y Win32