win32程序的執行單元
代碼的執行單元 ---- 線程
CreateProcess函數創建了進程,同時也創建了進程的主線程。系統中的每個進程都至少有一個線程。
多線程
創建新的線程函數:CreateThread()
等待線程運行結束:WaitForSingleObject(), 該函數用於等待指定對象變成受信狀態。
一個可執行對象有兩種狀態:未受信和受信狀態。線程對象只有當線程運行結束時才達到未受信狀態。
線程內核對象:
每一次CreateThread函數的成功調用,系統都會在內部爲新的線程分配一個內核對象。系統提供的管理線程的函數其實就是依靠訪問
線程內核對象來實現管理的。包含以下幾個基本成員:
1.CONTEXT(上下文, 即寄存器的狀態):反應線程上次運行時CPU寄存器的狀態。
2.Usage Count 使用計數:只要線程沒有結束運行,Usage Count的值至少爲1。創建一個新的線程後,初始狀態下Usage Count的值
是2。每打開一次該內核對象,計數值就加1。
3.Suspend Count 暫停計數:初始化爲1(暫停狀態), 這可以阻止新創建的線程被調度到CPU中,當線程的暫停計數是0的時候,該線
程就處於可調度的狀態。 單個線程可以被暫停若干次,如果一個線程被暫停了3次,它必須被喚醒3次纔可以分配給一個CPU。
喚醒線程:ResumeThread(HANDLE hThread) 可以喚醒一個掛起的線程, 減少計數
暫停線程:SuspendThread(HANDLE hThread) 可以掛起一個線程, 增加計數
4.Exit Code 退出代碼:指定了線程的退出代碼,也可以說是線程函數的返回值。在線程運行期間,線程函數還沒有返回,Exit Code
的值是STILL_ACTIVE。線程運行結束後,系統自動將Exit Code設爲線程函數的返回值。GetExitCodeThread()函數得到線程的退出代
碼。
5.Signaled 是否受信:線程運行期間,一直是未受信狀態,只有當線程結束後,系統才把Signaled的值設爲TRUE,即受信狀態。、
線程的終止 最後的方法是讓線程函數自然返回
使用ExitThread函數:終止當前線程的運行,促使系統釋放掉所有此線程使用的資源,但是,C/C++資源卻得不到正確的清除。
使用TerminateThread函數:在一個線程中強制終止另一個線程的執行。但是,系統不會釋放線程使用的堆棧。
線程同步
同步可以保證一個時間內只有一個線程對某個共享資源有控制權。
共享資源:包括全局變量、公共數據成員、句柄等。
1.臨界區對象:
臨界區對象是定義在數據段中的一個CRITICAL_SECTION結構,windows內部使用這個結構記錄一些信息,確保在同一時間只有一
個線程訪問該數據段中的數據。
初始化:InitialzeCriticalSection()
進入:EnterCriticalSection()
退出:LeaveCriticalSection()
刪除:DeleteCriticalSection()
2.互鎖函數:
互鎖函數爲同步訪問多線程共享變量提供了一個簡單的機制。
InterlockedIncrement():遞增變量
InterlockedDecrement():遞減變量
3.事件內核對象:
事件對象(event)是一種抽象的對象,它也有未受信和受信兩種狀態,不同於其他內核對象的是,一下函數可以使事件內核對象在這
兩種狀態之間轉化。事件對象主要用於線程間通信,因爲它是一個內核對象,所以也可以跨進程使用。
創建事件對象:CreateEvent()
4.信號量內核對象:
信號量(Semaphore)內核對象允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程的數目。
信號量的使用特點使其更適用於對Socket程序中線程的同步。
5.互斥內核對象:Mutex
只有擁有互斥內核對象的線程才具有訪問資源的權限。與其它幾種內核對象不同,互斥對象在操作系統中擁有特殊代碼,並由操作
系統來管理,操作系統甚至還允許其進行一些其它內核對象不能進行的非常規操作。
6.線程局部存儲(TLS: Thread Local Storage):
全局變量與函數內定義的靜態變量是各個函數都可以訪問的共享變量,如果需要一個在線程內部的各個函數都可以訪問,但其它線
程不能訪問的變量(被稱爲線程局部靜態變量),這時就需要TLS。
線程局部存儲在不同的平臺有不同的實現,可移植性不太好,幸好實現線程局部存儲並不難,最簡單的方法就是建立一個全局表,
通過當前線程ID去查詢相應的數據,因爲各個線程的ID不同,查詢的數據自然也就不同。
windows中如何實現線程局部存儲:
windows系統採用了每個線程建線程專享的索引表,表的條目是線程局部存儲的地址。windows僅爲系統中的每一個進程維護一個位
數組,再爲該進程中的每一個線程申請一個同樣長度的數組空間。
進程中的線程局部存儲位數組,指示了本進程的線程數組中哪一個成員在使用中。
動態使用TLS的步驟:
(1).主線程調用TlsAlloc函數爲線程局部存儲分配索引:
函數原型:DWORD TlsAlloc(void); 返回一個TLS索引;
(2).每個線程調用TlsSetValue和TlsGetValue設置或讀取線程數組中的值:
(3).主線程調用TlsFree釋放局部存儲索引:該函數唯一的參數是TlsAlloc返回的索引。