Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Функция 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.
Note
Современные варианты C++: Для нового кода рекомендуется использовать std::thread (C++11) или std::jthread (C++20 с автоматическим присоединением и совместной отменой через std::stop_token). Они обеспечивают типобезопасное управление потоками на основе RAII. Используйте CreateThread , если вам нужны специальные функции Win32, такие как атрибуты безопасности, элемент управления размером стека или приостановленное создание потока. Для асинхронной работы, не требующей выделенного потока, предпочтительны std::async, API пула потоков Windows или сопрограммы C++/WinRT (co_await).
Рискованно передавать адрес локальной переменной, если поток, который её создаёт, завершается раньше нового потока, поскольку указатель становится недействительным. Вместо этого либо передавайте указатель на динамически выделенную память, либо сделайте так, чтобы поток, создающий новый поток, дождался его завершения. Данные также можно передать из создаваемого потока в новый поток с помощью глобальных переменных. При использовании глобальных переменных обычно необходимо синхронизировать доступ несколькими потоками. Дополнительные сведения о синхронизации см. в разделе "Синхронизация выполнения нескольких потоков".
Создающий поток может использовать аргументы CreateThread для указания следующего:
- Атрибуты безопасности для дескриптора, указывающего на новый поток. Эти атрибуты безопасности включают флаг наследования, определяющий, может ли дескриптор наследоваться дочерними процессами. Атрибуты безопасности также включают дескриптор безопасности, который система использует для выполнения проверок доступа при всех последующих обращениях к дескриптору потока до предоставления доступа.
- Начальный размер стека нового потока. Стек потока выделяется автоматически в пространстве памяти процесса; система увеличивает стек по мере необходимости и освобождает его при завершении потока. Дополнительные сведения см. в разделе "Размер стека потоков".
- Флаг создания, позволяющий создать поток в приостановленном состоянии. При приостановке поток не запускается до вызова функции ResumeThread .
Вы также можете создать поток, вызвав функцию CreateRemoteThread . Эта функция используется процессами отладчика для создания потока, который выполняется в адресном пространстве отлаживаемого процесса.
Связанные темы