Vytváření vláken

Funkce CreateThread vytvoří nové vlákno pro proces. Vytvářející vlákno musí zadat počáteční adresu kódu, který má nové vlákno začít vykonávat. Počáteční adresa je obvykle název funkce definované v kódu programu (další informace najdete v tématu ThreadProc). Tato funkce přebírá jeden parametr a vrací hodnotu DWORD . Proces může mít více vláken současně, které spouští stejnou funkci.

Následuje jednoduchý příklad, který ukazuje, jak vytvořit nové vlákno, které spouští místně definovanou funkci , MyThreadFunction.

Volající vlákno používá funkci WaitForMultipleObjects k zachování, dokud se neukončí všechna pracovní vlákna. Volající vlákno je během čekání blokováno; pro pokračování ve zpracování volající vlákno použije WaitForSingleObject a čeká, až každé pracovní vlákno signalizuje svůj objekt čekání. Všimněte si, že pokud byste zavřeli popisovač pracovního vlákna dříve, než se ukončí, pracovní vlákno se tím neukončí. Tento identifikátor však nebude k dispozici pro použití při následných voláních funkce.

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

Funkce MyThreadFunction se vyhne použití knihovny CRT (Run-time) jazyka C, protože řada jejích funkcí není bezpečná pro přístup z více vláken, zejména pokud nepoužíváte multithreaded CRT. Pokud chcete použít CRT ve funkci ThreadProc, použijte místo toho funkci _beginthreadex.

Note

Moderní alternativy jazyka C++: U nového kódu zvažte použití std::thread (C++11) nebo std::jthread (C++20 s automatickým připojením a zrušením spolupráce prostřednictvím std::stop_token). Poskytují typově bezpečnou správu vláken založenou na RAII. CreateThread použijte, pokud potřebujete funkce specifické pro Win32, jako jsou atributy zabezpečení, nastavení velikosti zásobníku nebo vytvoření vlákna v pozastaveném stavu. Pro asynchronní práci, která nevyžaduje vyhrazené vlákno, upřednostněte std::async, rozhraní API fondu vláken systému Windows, nebo korutiny C++/WinRT (co_await).

Je riskantní předat adresu místní proměnné, pokud se vytváření vlákna ukončí před novým vláknem, protože ukazatel je neplatný. Místo toho buď předejte ukazatel na dynamicky přidělenou paměť, nebo nechte vlákno, které nové vlákno vytvořilo, počkat na jeho ukončení. Data lze také předat z vlákna pro vytváření do nového vlákna pomocí globálních proměnných. U globálních proměnných je obvykle nutné synchronizovat přístup několika vlákny. Další informace o synchronizaci naleznete v tématu Synchronizace provádění více vláken.

Vytvoření vlákna může použít argumenty CreateThread k určení následujících:

  • Atributy zabezpečení pro popisovač nového vlákna. Tyto atributy zabezpečení zahrnují příznak dědičnosti, který určuje, zda deskriptor mohou dědit podřízené procesy. Atributy zabezpečení zahrnují také popisovač zabezpečení, který systém používá k provádění kontrol přístupu u všech následných použití popisovače vlákna před udělením přístupu.
  • Počáteční velikost zásobníku nového vlákna. Zásobník vlákna se přiděluje automaticky v paměťovém prostoru procesu; systém podle potřeby zvýší zásobník a uvolní ho při ukončení vlákna. Další informace naleznete v tématu Velikost zásobníku vláken.
  • Příznak vytvoření, který umožňuje vytvořit vlákno v pozastaveném stavu. Při pozastavení se vlákno nespustí, dokud nebude volána funkce ResumeThread .

Vlákno můžete vytvořit také voláním funkce CreateRemoteThread. Tuto funkci používají procesy ladicího programu k vytvoření vlákna, které se spouští v adresním prostoru procesu, který se ladí.

Ukončení vlákna