Создание потоков

Функция CreateThread создает новый поток для процесса. Создаваемый поток должен указать начальный адрес кода, который должен выполнить новый поток. Как правило, начальный адрес — это имя функции, определенной в коде программы (дополнительные сведения см. в разделе ThreadProc). Эта функция принимает один параметр и возвращает значение DWORD . Процесс может содержать несколько потоков, одновременно выполняющих одну и ту же функцию.

Ниже приведен простой пример, демонстрирующий создание нового потока, который выполняет локально определенную функцию . MyThreadFunction

Вызывающий поток использует функцию WaitForMultipleObjects для сохранения до завершения всех рабочих потоков. Вызывающий поток блокируется во время ожидания; Чтобы продолжить обработку, вызывающий поток будет использовать WaitForSingleObject и ожидать, пока каждый рабочий поток сообщит о своем объекте ожидания. Обратите внимание, что при закрытии дескриптора в рабочем потоке перед его завершением рабочий поток не будет завершен. Однако дескриптор будет недоступен для использования в последующих вызовах функций.

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

Функция MyThreadFunction избегает использования библиотеки времени выполнения C (CRT), так как многие из ее функций не являются потокобезопасны, особенно если вы не используете многопоточный CRT. Если вы хотите использовать CRT в ThreadProc функции, используйте вместо нее функцию _beginthreadex .

Если создающий поток завершает работу перед новым потоком, передать адрес локальной переменной рискованно, так как указатель становится недопустимым. Вместо этого передайте указатель на динамически выделенную память или заставьте создающий поток ожидать завершения нового потока. Данные также могут передаваться из создаваемого потока в новый поток с помощью глобальных переменных. При использовании глобальных переменных обычно необходимо синхронизировать доступ нескольких потоков. Дополнительные сведения о синхронизации см. в разделе Синхронизация выполнения нескольких потоков.

Создаваемый поток может использовать аргументы createThread , чтобы указать следующее:

  • Атрибуты безопасности для дескриптора для нового потока. Эти атрибуты безопасности включают флаг наследования, который определяет, может ли дескриптор наследоваться дочерними процессами. Атрибуты безопасности также включают дескриптор безопасности, который система использует для выполнения проверок доступа ко всем последующим использованию дескриптора потока перед предоставлением доступа.
  • Начальный размер стека нового потока. Стек потока автоматически выделяется в памяти процесса; система увеличивает стек по мере необходимости и освобождает его при завершении потока. Дополнительные сведения см. в разделе Размер стека потоков.
  • Флаг создания, позволяющий создать поток в приостановленном состоянии. При приостановке поток не запускается до вызова функции ResumeThread .

Вы также можете создать поток, вызвав функцию CreateRemoteThread . Эта функция используется процессами отладчика для создания потока, который выполняется в адресном пространстве отлаживаемого процесса.

Завершение потока