CreateThread函數創建一個進程的新的線程。創建線程必須指定新線程要執行的代碼的起始地址。通常,起始地址是程序代碼中定義的函數的名稱(有關更多信息,請參閱ThreadProc)。此函數采用單個參數并返回DWORD
值。一個進程可以讓多個線程同時執行相同的功能。
以下是演示如何創建執行本地定義函數的新線程的簡單示例MyThreadFunction
。
調用線程使用WaitForMultipleObjects函數持久化,直到所有工作線程終止。調用線程在等待時阻塞; 為了繼續處理,調用線程將使用WaitForSingleObject并等待每個工作線程發信號通知其等待對象。請注意,如果要在終止之前關閉工作線程的句柄,則不會終止工作線程。但是,句柄將不可用于后續函數調用。
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>#define MAX_THREADS 3
#define BUF_SIZE 255DWORD WINAPI MyThreadFunction( LPVOID lpParam );
void ErrorHandler(LPTSTR 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 attributes0, // use default stack size MyThreadFunction, // thread function namepDataArray[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(LPTSTR 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。如果要在ThreadProc
函數中使用CRT ,請改用_beginthreadex
函數。
如果創建線程在新線程之前退出,則傳遞局部變量的地址是有風險的,因為指針變為無效。相反,要么將指針傳遞給動態分配的內存,要么讓創建線程等待新線程終止。數據也可以使用全局變量從創建線程傳遞到新線程。對于全局變量,通常需要通過多個線程同步訪問。有關同步的詳細信息,請參閱同步多個線程的執行。
創建線程可以使用CreateThread的參數來指定以下內容:
新線程句柄的安全屬性。這些安全屬性包括一個繼承標志,用于確定子進程是否可以繼承句柄。安全性屬性還包括安全描述符,系統使用該描述符在授予訪問權限之前對線程句柄的所有后續使用執行訪問檢查。
新線程的初始堆棧大小。線程的堆棧自動分配在進程的內存空間中; 系統根據需要增加堆棧,并在線程終止時釋放它。有關更多信息,請參閱線程堆棧大小。
一個創建標志,使您可以創建處于掛起狀態的線程。掛起時,線程不會運行,直到調用ResumeThread函數。
您還可以通過調用CreateRemoteThread函數來創建線程。調試器進程使用此函數來創建在正在調試的進程的地址空間中運行的線程。
相關話題
終止線程