Condividi tramite


Creazione di thread

La funzioneCreateThreadcrea un nuovo thread per un processo. Il thread di creazione deve specificare l'indirizzo iniziale del codice che deve essere eseguito dal nuovo thread. In genere, l'indirizzo iniziale è il nome di una funzione definita nel codice del programma . Per altre informazioni, vedere ThreadProc). Questa funzione accetta un singolo parametro e restituisce un valore DWORD. Un processo può avere più thread contemporaneamente che eseguono la stessa funzione.

Di seguito è riportato un semplice esempio che illustra come creare un nuovo thread che esegue la funzione definita localmente, MyThreadFunction.

Il thread chiamante utilizza la funzione WaitForMultipleObjects per attendere fino a quando tutti i thread di lavoro non sono terminati. Il thread chiamante si blocca mentre è in attesa; per continuare l'elaborazione, un thread chiamante userà WaitForSingleObject e attenderà che ogni thread di lavoro segnali l'oggetto di attesa. Si noti che se si chiude l'handle di un thread di lavoro prima della sua terminazione, questo non termina il thread di lavoro. Tuttavia, l'handle non sarà disponibile all'uso nelle chiamate di funzione successive.

#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 funzione MyThreadFunction evita l'uso della libreria di runtime C (CRT), poiché molte delle relative funzioni non sono thread-safe, in particolare se non si usa il CRT multithreading. Se si vuole usare CRT in una funzione ThreadProc, usare invece la funzione _beginthreadex.

È rischioso passare l'indirizzo di una variabile locale se il thread di creazione viene chiuso prima del nuovo thread, perché il puntatore diventa non valido. Passare invece un puntatore alla memoria allocata dinamicamente o fare in modo che il thread di creazione attenda il termine del nuovo thread. I dati possono anche essere passati dal thread di creazione al nuovo thread usando variabili globali. Con le variabili globali, in genere è necessario sincronizzare l'accesso da più thread. Per altre informazioni sulla sincronizzazione, vedere Sincronizzazione dell'esecuzione di più thread.

Il thread di creazione può usare gli argomenti per CreateThread per specificare quanto segue:

  • Attributi di sicurezza dell'handle per il nuovo thread. Questi attributi di sicurezza includono un flag di ereditarietà che determina se l'handle può essere ereditato dai processi figlio. Gli attributi di sicurezza includono anche un descrittore di sicurezza, usato dal sistema per eseguire controlli di accesso su tutti gli usi successivi dell'handle del thread prima che venga concesso l'accesso.
  • Dimensioni dello stack iniziale del nuovo thread. Lo stack del thread viene allocato automaticamente nello spazio di memoria del processo; il sistema aumenta lo stack in base alle esigenze e lo libera quando termina il thread. Per ulteriori informazioni, vedere Dimensione dello Stack del Thread.
  • Flag di creazione che consente di creare il thread in uno stato sospeso. Quando sospeso, il thread non viene eseguito finché non viene chiamata la funzione ResumeThread.

È anche possibile creare un thread chiamando la funzione CreateRemoteThread. Questa funzione viene usata dai processi del debugger per creare un thread eseguito nello spazio indirizzi del processo di cui viene eseguito il debug.

Terminare un thread