Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A função CreateThread cria uma nova thread para um processo. O thread de criação deve especificar o endereço inicial do código que o novo thread deve executar. Normalmente, o endereço inicial é o nome de uma função definida no código do programa (para mais informações, veja ThreadProc). Esta função toma um único parâmetro e devolve um valor DWORD . Um processo pode ter várias threads a executar a mesma função em simultâneo.
Segue-se um exemplo simples que demonstra como criar um novo thread que execute a função localmente definida, MyThreadFunction.
A thread que chama utiliza a função WaitForMultipleObjects para persistir até que todas as threads worker terminem. A thread de invocação bloqueia enquanto está à espera; para continuar o processamento, uma thread de invocação usaria WaitForSingleObject e aguardaria que cada thread de trabalho sinalizasse o respetivo objeto de espera. Tenha em atenção que, se fechasse o identificador de uma thread de trabalho antes de esta terminar, isso não terminaria a thread de trabalho. No entanto, a alavanca não estará disponível para uso em chamadas de função subsequentes.
#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);
}
A função MyThreadFunction evita o uso da biblioteca de runtime de C (CRT), pois muitas das suas funções não são seguras em ambientes multithread, especialmente se não estiver a usar o CRT multithread. Se pretender utilizar o CRT numa função ThreadProc, utilize a função _beginthreadex em vez desta.
Note
Alternativas modernas a C++: Para novo código, considere usar std::thread (C++11) ou std::jthread (C++20, com junção automática e cancelamento cooperativo via std::stop_token). Estes fornecem gestão de threads baseada em RAII, segura em termos de tipo. Utilize CreateThread quando necessitar de funcionalidades específicas do Win32, como atributos de segurança, controlo do tamanho da pilha ou criação suspensa de threads. Para trabalho assíncrono que não requer uma thread dedicada, prefira std::async, a API do pool de threads do Windows, ou corutinas C++/WinRT (co_await).
É arriscado passar o endereço de uma variável local se a thread que a criou terminar antes da nova thread, porque o ponteiro torna-se inválido. Em vez disso, passe um ponteiro para memória alocada dinamicamente ou faça com que a thread criadora espere que a nova thread termine. Os dados também podem ser passados do thread de criação para o novo thread usando variáveis globais. Com variáveis globais, é geralmente necessário sincronizar o acesso por múltiplos threads. Para mais informações sobre sincronização, consulte Sincronização da Execução de Múltiplos Threads.
O thread de criação pode usar os argumentos do CreateThread para especificar o seguinte:
- Os atributos de segurança do handle para a nova thread. Estes atributos de segurança incluem uma bandeira de herança que determina se o handle pode ser herdado por processos filhos. Os atributos de segurança incluem também um descritor de segurança, que o sistema utiliza para realizar verificações de acesso em todas as utilizações subsequentes do handle do thread antes de ser concedido o acesso.
- O tamanho inicial da pilha do novo tópico. A pilha da thread é alocada automaticamente no espaço de memória do processo; O sistema aumenta a pilha conforme necessário e liberta-a quando a thread termina. Para mais informações, consulte Tamanho da Pilha de Fios.
- Um flag de criação que permite criar o thread num estado suspenso. Quando suspensa, a thread não é executada até que a função ResumeThread seja chamada.
Também pode criar um thread chamando a função CreateRemoteThread . Esta função é usada por processos de depuração para criar uma thread que corre no espaço de endereçamento do processo a ser depurado.
Tópicos relacionados