Skapa trådar

Funktionen CreateThread skapar en ny tråd för en process. Den skapande tråden måste ange startadressen för den kod som den nya tråden ska köra. Startadressen är vanligtvis namnet på en funktion som definierats i programkoden (mer information finns i ThreadProc). Den här funktionen tar en enskild parameter och returnerar ett DWORD- värde. En process kan ha flera trådar samtidigt som samma funktion körs.

Följande är ett enkelt exempel som visar hur du skapar en ny tråd som kör den lokalt definierade funktionen MyThreadFunction.

Den anropande tråden använder funktionen WaitForMultipleObjects för att kvarstå tills alla arbetstrådar har avslutats. Den anropande tråden blockeras medan den väntar. för att fortsätta bearbetningen använder en anropande tråd WaitForSingleObject och väntar på att varje arbetstråd ska signalera sitt vänteobjekt. Observera att om du skulle stänga handtaget till en arbetstråd innan den avslutades avslutas inte arbetstråden. Handtaget är dock inte tillgängligt för användning i efterföljande funktionsanrop.

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

Funktionen MyThreadFunction undviker användning av C-körningsbiblioteket (CRT), eftersom många av dess funktioner inte är trådsäkra, särskilt om du inte använder den flertrådade CRT:en. Om du vill använda CRT i en ThreadProc-funktion använder du funktionen _beginthreadex i stället.

Det är riskabelt att skicka adressen till en lokal variabel om den skapande tråden avslutas före den nya tråden, eftersom pekaren blir ogiltig. Skicka i stället en pekare till dynamiskt allokerat minne eller få den skapande tråden att vänta tills den nya tråden avslutas. Data kan också skickas från den skapande tråden till den nya tråden med hjälp av globala variabler. Med globala variabler är det vanligtvis nödvändigt att synkronisera åtkomsten med flera trådar. Mer information om synkronisering finns i Synkronisering av körning av flera trådar.

Den skapande tråden kan använda argumenten till CreateThread för att ange följande:

  • Säkerhetsattributen för handtaget till den nya tråden. Dessa säkerhetsattribut innehåller en arvsflagga som avgör om handtaget kan ärvas av barnprocesser. Säkerhetsattributen innehåller också en säkerhetsbeskrivare, som systemet använder för att utföra åtkomstkontroller på alla efterföljande användningar av trådhandtaget innan åtkomst beviljas.
  • Den nya trådens ursprungliga stackstorlek. Trådens stack allokeras automatiskt i processens minnesutrymme. systemet ökar stacken efter behov och frigör den när tråden avslutas. För mer information, se stackstorlek för tråd.
  • En skapandeflagga som gör att du kan skapa tråden i pausat tillstånd. När tråden pausas körs den inte förrän funktionen ResumeThread anropas.

Du kan också skapa en tråd genom att anropa funktionen CreateRemoteThread. Den här funktionen används av felsökningsprocesser för att skapa en tråd som körs i adressutrymmet för den process som debuggas.

avsluta en tråd