C++拾遺--多線程:多線程的引入
多線程是編程中的一個重要內容。多核時代使多線程成為一種可能,顯然,一件事情多個人干,效率一定會提升。下面來看下C語言中是如何使用多線程的。
先來看一個實例
#define _CRT_SECURE_NO_WARNINGS #include運行#include #include #include DWORD WINAPI run(void *p) { char *mess = (char*)p; printf(線程%d,彈窗 , GetCurrentThreadId()); char threadId[20]; sprintf(threadId, 線程%d, GetCurrentThreadId()); MessageBoxA(0, mess, threadId, 0); return 0; } int main(void) { printf(******C語言多線程演示***by David*** ); char *mess[] = { 123, 456, 789 }; HANDLE handles[3]; for (int i = 0; i < sizeof(mess) / sizeof(*mess); i++) { handles[i] = CreateThread(NULL, 0, run, mess[i], 0, NULL); } WaitForMultipleObjects(3, handles, 1, INFINITE); return 0; }
異步彈出了三個窗口,並打印了各自的線程號。若是有沒看懂的地方,下面有詳細解釋:
1.handle是句柄,在windows中用句柄來標識對象。本質很簡單 typedef void * HANDLE;
2.CreateThread()用來創建線程。原型
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //內核對象的安全屬性
SIZE_T dwStackSize, //線程棧大小
LPTHREAD_START_ROUTINE lpStartAddress, //線程函數地址
LPVOID lpParameter, //傳給線程函數的參數
DWORD dwCreationFlags, //控制位
LPDWORD lpThreadId //獲取線程id
);
參數解釋:
第一個參數是線程內核對象的安全屬性,一般傳入NULL表示使用默認設置。
第二個參數是線程棧空間的大小。傳入0表示使用默認大小(1MB)。
第三個參數是新線程所執行的線程函數地址,多個線程可以使用同一個函數地址。
第四個參數是傳給線程函數的參數。typedef void * LPVOID
第五個參數是用來控制線程的創建,0表示創建後立即執行。
第六個參數是傳出參數,用來獲得線程的id。顯然,傳入NULL,表示調用者並不想知道線程的id。
返回值:線程句柄
3.線程函數的聲明。#define WINAPI __stdcall (vs2013)typedef unsigned long DWORD。其中,__stdcall是指C/CPP中函數的調用方式。主要有兩點:1.實參從右向左入棧。2.調用者負責清空參數棧。
4.線程等待函數
DWORD WINAPI WaitForMultipleObjects(
DWORD nCount, //內核對象的個數
CONST HANDLE *lpHandles, //句柄數組的地址
BOOL bWaitAll, //是否等待所有
DWORD dwMilliseconds //等待的最大時間,單位毫秒,INFINITE表示無限等待
);
函數功能:讓線程進入等待轉態,直到條件觸發。內核對象在運行期間處於未觸發的狀態,直到執行結束。
5.線程函數類型是
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
更簡潔的是
typedef unsigned long (__stdcall *pfun)(void*);
函數原型
uintptr_t __cdecl _beginthreadex(
void* _Security,
unsigned _StackSize,
_beginthreadex_proc_type _StartAddress,
void* _ArgList,
unsigned _InitFlag,
unsigned* _ThrdAddr
);
它的參數類型和CreateThread基本一致,只是線程函數類型稍有不同。線程函數類型是
typedef unsigned (__stdcall *_beginthreadex_proc_type)(void*);
CreateThread的調用過於復雜,下面我們玩兒個簡單的,我們用多個線程打印 Hello World
#include運行#include #include #include void hello(void *p) { printf(線程%d, say Hello World , GetCurrentThreadId()); } int main() { printf(******C語言多線程演示***by David*** ); HANDLE handles[5]; for (int i = 0; i < 5; i++) { handles[i] = _beginthread(hello, 0, NULL); } WaitForMultipleObjects(5, handles, 1, INFINITE); getchar(); return 0; }
_beginthread的原型
uintptr_t _beginthread(
_beginthread_proc_type _StartAddress, //線程函數的地址
unsigned _StackSize, //線程棧的大小
void* _ArgList //線程函數的參數
);
函數功能:使用指定線程函數創建線程,並返回線程句柄。
幾點解釋:
1.typedef unsigned int * uintptr_t;
2.typedef void(__cdecl *_beginthread_proc_type)(void*); _beginthread_proc_type就是一函數指針類型,我們提供的線程函數應該如此設計:只有一個參數,類型為void*,且返回值類型是void。
CreateThread和_beginthread的使用說明:
從函數參數可以看出,CreateThread用於對所創建的線程進行精細控制。在很多參數處於默認設置下,建議使用參數簡單的_beginthread。兩者所需的線程函數類型不同。
使用多線程,就要先寫好線程函數,然後調用相關函數創建線程即可。由於_beginthread傳參簡單,一般情況下,使用_beginthread創建多線程。