多線程同步互斥,互斥是指某一資源一段時間內只允許一個訪問者進行訪問,同步是指在互斥的基礎上,多個線程或進程按照一定順序來運行。
c++語言本身沒有提供多線程機制,windows系統為我們提供了相關的API。
windowsAPI提供了函數CreateThread(),其基本過程為:
1.在核心對象中分配一個線程標識/控制代碼,可供管理,由CreateThread返回
2.把線程退出碼置為STILL_ACTIVE,把線程掛起計數置1
3.分配context結構
4.分配兩頁的實體儲存體以準備棧,保護頁設定為PAGE_READWRITE,第2頁設為PAGE_GUARD
5.lpStartAddr和lpvThread值被放在棧頂,使它們成為傳送給StartOfThread的參數
6.把context結構的棧指標指向棧頂(第5步)指令指標指向startOfThread函數
函數為
HANDLEWINAPICreateThread(
LPSECURITY_ATTRIBUTESlpThreadAttributes,
SIZE_TdwStackSize,
LPTHREAD_START_ROUTINElpStartAddress,
LPVOIDlpParameter,
DWORDdwCreationFlags,
LPDWORDlpThreadId
);
第一個參數為線程核心安全屬性,預設為NULL,
第二個參數為線程棧的空間大小,傳入0表示使用預設大小1MB,而windows也會根據情況來動態延長堆棧大小。
第三個參數為線程函數地址,使用DWORDWINAPIThreadProc(LPVOIDpParam);來聲明一個線程函數。(DWORD為雙字即32bit,4位元組,LPVOID為void指標,指向函數的參數)
第四個參數為傳給線程函數的參數。
第五個函數指定額外的標誌來控制線程的建立,為0表示線程建立後立即調度,為CREATE_SUSPENDED表示線程建立後暫停運行,直到調用RESUMETHREAD(HANDLE);
第六個參數返回線程ID號,若為NULL,則不返回,為指標則將ID賦值個指標指向的地址。
多線程中常用的幾個函數:
GetCurrentThreadId()返回線程ID。
WaitForSingleObject
函數功能:等待函數–使線程進入等待狀態,直到指定的核心對象被觸發。
函數原形:
DWORDWINAPIWaitForSingleObject(
HANDLEhHandle,
DWORDdwMilliseconds
);
函數說明:
第一個參數為要等待的核心對象。
第二個參數為最長等待的時間,以毫秒為單位,如傳入5000就表示5秒,傳入0就立即返回,傳入INFINITE表示無限等待。
因為線程的控制代碼線上程運行時是未觸發的,線程結束運行,控制代碼處於觸發狀態。所以可以用WaitForSingleObject()來等待一個線程結束運行。
函數傳回值:
在指定的時間內對象被觸發,函數返回WAIT_OBJECT_0。超過最長等待時間對象仍未被觸發返回WAIT_TIMEOUT。傳入參數有錯誤將返回WAIT_FAILED。
等待數組iphandles中所有線程控制代碼執行結束。
DWORDWaitForMultipleObjects(
DWORDnCount,//數組長度
constHANDLE*lpHandles,//數組指標
BOOLbWaitAll,//是否等待全部線程
DWORDdwMilliseconds//最大等待時間
);
不推薦使用CREATETHREAD的原因是c語言中為考慮多線程,如使用全域變數errno,則對於strerror()、strtok()、tmpnam()、gmtime()等函數會線程不安全,所以編譯器建議使用這些函數的安全執行緒的新版本,或者使用安全執行緒的函數_beginthreadex()。
_beginthreadex():
這個函數對於標準c庫中進行的安全執行緒的解決辦法是對於任意一個線程,配置一塊專有的記憶體區來供c庫中的函數使用。
這裡發現,其實對於現在的編譯器,已經不必使用這個函數了,因為不使用之前幾個函數的安全執行緒型新函數就給編譯錯誤,則,這個函數的安全執行緒的優勢就沒了,而且對於函數已經安全執行緒的情況下,這個函數建立一個記憶體區來儲存資訊明顯是浪費空間和時間的選擇。
這個函數大致參數與CreateThread相同,只是傳回值類型為uintptr_t,則對於建立一個線程並返回線程控制代碼需要強制轉換為HANDLE類型,
線程函數的類型為unsigned__stdcallfun(void*);
//第1個參數:安全屬性,NULL為預設安全屬性//第2個參數:指定線程堆棧的大小。如果為0,則線程堆棧大小和建立它的線程的相同。一般用0//第3個參數:指定線程函數的地址,也就是線程調用執行的函數地址(用函數名稱即可,函數名稱就表示地址)//第4個參數:傳遞給線程的參數的指標,可以通過傳入對象的指標,線上程函數中再轉化為對應類的指標//第5個參數:線程初始狀態,0:立即運行;CREATE_SUSPEND:suspended(懸掛)//第6個參數:用於記錄線程ID的地址uintptr_t_beginthreadex(//NATIVECODEvoid*security,unsignedstack_size,unsigned(__stdcall*start_address)(void*),void*arglist,unsignedinitflag,unsigned*thrdaddr);