Partage via


Exemple de programme multithread en langage C

Bounce.c est un exemple de programme multithread qui crée un thread chaque fois que la lettre a ou A est tapée. Chaque thread rebondit une lettre d’une couleur différente autour de l’écran. Jusqu’à 32 threads peuvent être créés. L’arrêt normal du programme se produit quand q ou Q est typé.

Les programmes sont compilés en tant que multithread par défaut.

  1. Dans le menu Fichier, choisissez Nouveau>Projet.

  2. Dans la boîte de dialogue Créer un projet , sélectionnez le modèle d’application console qui a des balises C++, Windows et Console . Choisissez Suivant pour continuer.

  3. Dans la boîte de dialogue Configurer votre nouveau projet , entrez un nom pour votre projet, tel que « Rebond ». Choisissez Créer pour continuer.

  4. Dans la fenêtre Explorateur de solutions, ouvrez le dossier Fichiers sources sous votre projet et modifiez le nom de votre fichier source pour avoir une extension .c.

  5. Dans la fenêtre d’édition, supprimez le code source existant et remplacez-le par l’exemple de code.

  6. Dans le menu Générer , choisissez Générer la solution.

  7. Appuyez sur F5 pour démarrer le programme dans le débogueur.

  1. Dans le menu Fichier, choisissez Nouveau>Projet.

  2. Dans la boîte de dialogue Nouveau projet , sélectionnez Visual C++ dans le volet gauche, puis sélectionnez Projet vide dans le volet central.

  3. Dans la zone De modification nom , entrez un nom pour votre projet, par exemple « Rebond ». Choisissez OK pour créer le projet vide.

  4. Dans la fenêtre Explorateur de solutions, ouvrez le dossier Fichiers sources sous votre projet et ajoutez le fichier contenant le code source C au projet.

  5. Dans le menu Générer , générez le projet en choisissant la commande Générer la solution .

  6. Appuyez sur F5 pour démarrer le programme dans le débogueur.

  7. Appuyez sur a pour créer un thread. Chaque thread rebondit un caractère d’une couleur différente autour de l’écran.

  8. Appuyez sur q pour quitter.

  1. Ouvrez une invite de commandes Outils Visual Studio. Cela garantit que le chemin d’accès est défini pour inclure le compilateur.

  2. Compilez et liez le programme :

    cl bounce.c
    

Exemple

Pour générer sur la ligne de commande, copiez et enregistrez cet exemple dans un fichier source avec une extension .c. Dans l’IDE, remplacez tout code source créé par le modèle par cet exemple :

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

Voir aussi

Multithreading à l’aide de C et de Win32