Programma multithread di esempio in linguaggio C

Bounce.c è un programma multithread di esempio che crea un nuovo thread ogni volta che la lettera a o A viene digitata. Ogni thread rimbalza una lettera di un colore diverso intorno allo schermo. È possibile creare fino a 32 thread. La terminazione normale del programma si verifica quando q o Q viene digitato.

I programmi vengono compilati come multithreading per impostazione predefinita.

  1. Nel menu File scegliere Nuovo>Progetto.

  2. Nella finestra di dialogo Crea un nuovo progetto selezionare il modello App console con tag C++, Windows e Console. Scegliere Avanti per continuare.

  3. Nella finestra di dialogo Configura il nuovo progetto immettere un nome per il progetto, ad esempio "Bounce". Scegliere Crea per continuare.

  4. Nella finestra Esplora soluzioni aprire la cartella File di origine nel progetto e modificare il nome del file di origine in modo che abbia un'estensione c.

  5. Nella finestra di modifica eliminare il codice sorgente esistente e sostituirlo con il codice di esempio.

  6. Scegliere Compila soluzione dal menu Compila.

  7. Premere F5 per avviare il programma nel debugger.

  1. Nel menu File scegliere Nuovo>Progetto.

  2. Nella finestra di dialogo Nuovo progetto selezionare Visual C++ nel riquadro sinistro e quindi selezionare Progetto vuoto nel riquadro centrale.

  3. Nella casella di modifica Nome immettere un nome per il progetto, ad esempio "Bounce". Scegliere OK per creare il progetto vuoto.

  4. Nella finestra Esplora soluzioni aprire la cartella File di origine nel progetto e aggiungere il file contenente il codice sorgente C al progetto.

  5. Nel menu Compila compilare il progetto scegliendo il comando Compila soluzione.

  6. Premere F5 per avviare il programma nel debugger.

  7. Premere un oggetto per creare un nuovo thread. Ogni thread rimbalza un carattere di un colore diverso intorno allo schermo.

  8. Premere q per uscire.

  1. Aprire un prompt dei comandi degli strumenti di Visual Studio. In questo modo, il percorso viene impostato in modo da includere il compilatore.

  2. Compilare e collegare il programma:

    cl bounce.c
    

Esempio

Per eseguire la compilazione sulla riga di comando, copiare e salvare questo esempio in un file di origine con estensione c. Nell'IDE sostituire qualsiasi codice sorgente creato dal modello con questo esempio:

// 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);
}

Vedi anche

Multithreading con C e Win32