Création de threads

La fonction CreateThread crée un thread pour un processus. Le thread de création doit spécifier l’adresse de départ du code à exécuter par le nouveau thread. En règle générale, l’adresse de départ est le nom d’une fonction définie dans le code du programme (pour plus d’informations, voir ThreadProc). Cette fonction prend un paramètre unique et retourne une valeur DWORD . Un processus peut avoir plusieurs threads simultanément exécutant la même fonction.

Voici un exemple simple qui montre comment créer un thread qui exécute la fonction définie localement. MyThreadFunction

Le thread appelant utilise la fonction WaitForMultipleObjects pour rester en attente jusqu’à ce que tous les threads de travail soient terminés. Le thread appelant bloque pendant qu’il attend ; pour poursuivre le traitement, un thread appelant utilise WaitForSingleObject et attend que chaque thread de travail signale son objet d’attente. Notez que si vous fermiez le handle d’un thread de travail avant son arrêt, cela ne met pas fin au thread de travail. Toutefois, le handle n’est pas disponible pour une utilisation dans les appels de fonction suivants.

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>

#define MAX_THREADS 3
#define BUF_SIZE 255

DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(LPCTSTR lpszFunction);

// Sample custom data structure for threads to use.
// This is passed by void pointer so it can be any data type
// that can be passed using a single void pointer (LPVOID).
typedef struct MyData {
    int val1;
    int val2;
} MYDATA, *PMYDATA;


int _tmain()
{
    PMYDATA pDataArray[MAX_THREADS];
    DWORD   dwThreadIdArray[MAX_THREADS];
    HANDLE  hThreadArray[MAX_THREADS]; 

    // Create MAX_THREADS worker threads.

    for( int i=0; i<MAX_THREADS; i++ )
    {
        // Allocate memory for thread data.

        pDataArray[i] = (PMYDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                sizeof(MYDATA));

        if( pDataArray[i] == NULL )
        {
           // If the array allocation fails, the system is out of memory
           // so there is no point in trying to print an error message.
           // Just terminate execution.
            ExitProcess(2);
        }

        // Generate unique data for each thread to work with.

        pDataArray[i]->val1 = i;
        pDataArray[i]->val2 = i+100;

        // Create the thread to begin execution on its own.

        hThreadArray[i] = CreateThread( 
            NULL,                   // default security attributes
            0,                      // use default stack size  
            MyThreadFunction,       // thread function name
            pDataArray[i],          // argument to thread function 
            0,                      // use default creation flags 
            &dwThreadIdArray[i]);   // returns the thread identifier 


        // Check the return value for success.
        // If CreateThread fails, terminate execution. 
        // This will automatically clean up threads and memory. 

        if (hThreadArray[i] == NULL) 
        {
           ErrorHandler(TEXT("CreateThread"));
           ExitProcess(3);
        }
    } // End of main thread creation loop.

    // Wait until all threads have terminated.

    WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);

    // Close all thread handles and free memory allocations.

    for(int i=0; i<MAX_THREADS; i++)
    {
        CloseHandle(hThreadArray[i]);
        if(pDataArray[i] != NULL)
        {
            HeapFree(GetProcessHeap(), 0, pDataArray[i]);
            pDataArray[i] = NULL;    // Ensure address is not reused.
        }
    }

    return 0;
}


DWORD WINAPI MyThreadFunction( LPVOID lpParam ) 
{ 
    HANDLE hStdout;
    PMYDATA pDataArray;

    TCHAR msgBuf[BUF_SIZE];
    size_t cchStringSize;
    DWORD dwChars;

    // Make sure there is a console to receive output results. 

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if( hStdout == INVALID_HANDLE_VALUE )
        return 1;

    // Cast the parameter to the correct data type.
    // The pointer is known to be valid because 
    // it was checked for NULL before the thread was created.
 
    pDataArray = (PMYDATA)lpParam;

    // Print the parameter values using thread-safe functions.

    StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"), 
        pDataArray->val1, pDataArray->val2); 
    StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
    WriteConsole(hStdout, msgBuf, (DWORD)cchStringSize, &dwChars, NULL);

    return 0; 
} 



void ErrorHandler(LPCTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code.

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message.

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK); 

    // Free error-handling buffer allocations.

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}

La MyThreadFunction fonction évite l’utilisation de la bibliothèque runtime C (CRT), car la plupart de ses fonctions ne sont pas thread-safe, en particulier si vous n’utilisez pas le CRT multithread. Si vous souhaitez utiliser le CRT dans une ThreadProc fonction, utilisez plutôt la fonction _beginthreadex .

Note

Alternatives C++ modernes : Pour le nouveau code, envisagez d’utiliser std::thread (C++11) ou std::jthread (C++20, avec l’annulation automatique et coopérative via std::stop_token). Ceux-ci fournissent une gestion des threads basée sur RAII de type sécurisé. Utilisez CreateThread lorsque vous avez besoin de fonctionnalités spécifiques à Win32, telles que des attributs de sécurité, un contrôle de taille de pile ou une création de thread suspendue. Pour le travail asynchrone qui ne nécessite pas de thread dédié, préférez std::async, l'API de pool de threads Windows ou les coroutines C++/WinRT (co_await).

Il est risqué de passer l’adresse d’une variable locale si le thread de création s’arrête avant le nouveau thread, car le pointeur devient non valide. Au lieu de cela, passez un pointeur à la mémoire allouée dynamiquement ou faites en sorte que le thread de création attende que le nouveau thread se termine. Les données peuvent également être transmises du thread de création au nouveau thread à l’aide de variables globales. Avec les variables globales, il est généralement nécessaire de synchroniser l’accès par plusieurs threads. Pour plus d’informations sur la synchronisation, consultez Synchronisation de l’exécution de plusieurs threads.

Le thread de création peut utiliser les arguments pour CreateThread pour spécifier les éléments suivants :

  • Attributs de sécurité pour le handle vers le nouveau thread. Ces attributs de sécurité incluent un indicateur d’héritage qui détermine si le handle peut être hérité par les processus enfants. Les attributs de sécurité incluent également un descripteur de sécurité, que le système utilise pour effectuer des vérifications d’accès sur toutes les utilisations suivantes du handle du thread avant l’octroi de l’accès.
  • Taille initiale de la pile du nouveau thread. La pile du thread est allouée automatiquement dans l’espace mémoire du processus ; le système augmente la pile en fonction des besoins et le libère lorsque le thread se termine. Pour plus d’informations, consultez La taille de la pile des threads.
  • Indicateur de création qui vous permet de créer le thread dans un état suspendu. En cas de suspension, le thread ne s’exécute pas tant que la fonction ResumeThread n’est pas appelée.

Vous pouvez également créer un thread en appelant la fonction CreateRemoteThread . Cette fonction est utilisée par les processus de débogage pour créer un thread qui s’exécute dans l’espace d’adressage du processus débogué.

Fin d’un thread