Windows 多線程編程總結
關鍵字:多線程 線程同步 線程池 內核對象 1 內核對象1 .1 內核對象的概念內核對象是內核分配的一個內存塊,這種內存塊是一個數(shù)據結構,表示內核對象的各種特征。并且只能由內核來訪問。應用程序若需要訪問內核對象,需要通過操作系統(tǒng)提供的函數(shù)來進行,不能直接訪問內核對象(Windows 從安全性方面來考慮的)。 內核對象通過 Create* 來創(chuàng)建,返回一個用于標識內核對象的句柄,這些句柄 (而不是內核對象)可在創(chuàng)建進程范圍內使用,不能夠被傳遞到其他進程中被使用。 1 .2 內核對象使用的計數(shù)因為內核對象的所有者是內核,而不是進程,所以何時撤銷內核對象由內核決定,而內核做這個決定的依據就是該內核對象是否仍然被使用。那么如何判斷內核對象是否被使用呢?可以通過內核對象的“使用計數(shù)”屬性,一旦這個值變成 0 了,內核就可以釋放該對象了。 1 .3 創(chuàng)建內核對象1 .3.1 進程與句柄表每個進程在初始化的時候,將被分配一個句柄表,該句柄表中只存儲內核對象的句柄,不存儲用戶對象的句柄。句柄表的詳細結構微軟沒有公布,但是大致包含三個內容:內核對象句柄,內核對象地址,訪問屏蔽標志。 微軟為何要將內核對象的句柄設置為進程相關的呢?理由有: l 不同的進程對內核對象的訪問權限是不同的,有必要區(qū)分對待 l 如果句柄是全局的,則一個進程可以控制另外一個進程的句柄,破壞另外一個進程的句柄。
1 .3.2 創(chuàng)建內核對象及操作系統(tǒng)內部機制利用 CreateSomeObject 的函數(shù)來創(chuàng)建內核對象。調用該函數(shù)的時候內核就為該對象分配一個內存塊,并進行初始化,然后內核再掃描該進程的句柄表,初始化一條記錄并放在句柄表中。 1 .3.3 進程中使用內核對象的內部機制假設函數(shù) F 使用某個內核對象,其參數(shù)為 Handle1 ,則該函數(shù)內部需要查找該進程的句柄表,找出參數(shù)句柄對應的記錄,然后才能使用該內核對象。 1 .4 關閉內核對象無論進程怎樣創(chuàng)建內核對象,在不使用該對象的時候都應當通過 Bool CloseHandle(HANDLE hobj) 來向操作系統(tǒng)聲明結束對該對象的訪問。為什么叫聲明呢?是因為此時也許還有其他進程對該對象的訪問,操作系統(tǒng)可能并不立即釋放該對象。操作系統(tǒng)需要做的是:從進程的句柄表中刪除該內核對象的記錄,另外再考察該內核對象的使用計數(shù)以決定是否需要釋放該對象。 1 .5 內核對象的共享說到共享,與之孿生的就是共享權限。 Windows 內核對象的共享有三種方式: 1 .5.1 繼承式共享(父子進程間)只有當進程是父子關系的時候,才能使用此種方式的共享。特別要注意的是繼承的是內核對象的句柄,內核對象本身是不具備繼承性。要達到這種繼承的效果需要做以下幾件事: l 在進程創(chuàng)建內核對象的時候,需要一個安全結構 sa ( SECURITY_ATTRIBUTES 類型,以向 OS 聲明對象的訪問方式)作為參數(shù)。繼承式共享需要將結構的成員 sa.bInheritHandle 設置為 TRUE 。此時 OS 內部的處理式將進程的句柄表中的該對象的訪問屏蔽字段設置成“可繼承”。 l 在創(chuàng)建子進程( CreateProcess 函數(shù))時,設置創(chuàng)建參數(shù) bInheritHandles 為 TRUE 。表示被創(chuàng)建的子進程可以繼承父進程中的所有可繼承內核對象。 OS 內部的處理是:復制父進程句柄表中的記錄到子進程的句柄表中,并使用相同的句柄值;為內核對象的使用計數(shù)器加 1 。 特別說明:子進程能夠繼承的的內核對象僅局限于父進程創(chuàng)建它的時候所擁有的可繼承內核對象。子進程誕生后,父進程再搞出什么可繼承的東西,子進程是不能用的。這就需要在子進程中使用繼承的內核對象的時候需要慎重,以確定內核對象是否已被繼承了。 利用 SetHandleinformation 方法可以隨時修改內核對象句柄的一些屬性,目前公開的句柄屬性有兩種,一種是該句柄是否能被繼承,另一種是該句柄是否能被關閉。 1 .5.2 同名共享同名共享,不需要共享進程之間存在父子關系。但局限于內核對象是否支持這種共享方式。創(chuàng)建內核對象的Create 函數(shù)中是否包含 pszName 是該內核對象是否支持同名共享的標志。 l 方法一:當 Process1 通過 CreateObject ( …”someName” )創(chuàng)建了一個名字為 someName 的內核對象后, Process2 也調用了 CreateObject ( …”someName” ),此時內核的動作是:在全局中查詢發(fā)現(xiàn)已經存在 someName1 的對象;為 Process2 的句柄表添加一條 Ojbect 的記錄,使用的句柄不確定;為 someName這個 Object 的引用計數(shù)器加 1 。 l 方法二: Process2 使用 OpenObject ( …”someName” )的方式來獲得對名 someName 的 Object 的句柄。用這種 Open 方法的時候,需要提供一個參數(shù)讓 OS 鑒權,以判定是否能夠以參數(shù)指定的方式來訪問內核對象。 1 .5.3 復制內核對象的句柄的方式共享跨進程邊界的內核對象共享的另外一個方法是通過 DuplicateHandle 來復制內核對象句柄。 如果要將 ProcessS 中的對象拷貝到 ProcessT 中則調用 DuplicateHandle 的進程一定要有對這兩個進程的訪問權,即句柄表中擁有這兩個進程內核對象的句柄記錄。 2 線程的一般概念2 .1 視圖l 進程只是線程的容器,從來不執(zhí)行任何東西 l 線程總是在某個進程中被創(chuàng)建 l 線程在進程的地址空間中執(zhí)行代碼 l 線程們共享進程中的所有內核對象 3 線程的創(chuàng)建HANDLE CreateThread( PSECURITY_ATTRIBUTES psa, DWORD cbStack, PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam, DWORD fdwCreate, PDWORD pdwThreadID); 《 Windows 核心編程》 P124 介紹說應當使用編譯器提供的線程創(chuàng)建函數(shù),而不應當直接使用 CreateThread 。 3 .1 CreateThread 調用的內核行為調用 CreateThread 后, OS 進行如下幾個動作: l 生成一個線程內核對象 l 在進程空間內為線程分配堆??臻g 因為線程的環(huán)境同于其所在進程的環(huán)境,所以創(chuàng)建的線程可以訪問進程中的所有資源,包括線程中所有的內核對象。 4 線程銷亡4 .1 終止線程的方式:l 線程函數(shù)返回(最好使用這個方式,可以保證:線程種創(chuàng)建的 C++ 對象正常析構; OS 釋放線程堆棧內存; OS 將線程的退出碼設置為線程函數(shù)的返回值;系統(tǒng)將遞減該線程內核對象的的使用計數(shù)器【如果此時還有其他引用 …… ,見下面說明】。) l 調用 ExitThread (不能釋放 C++ 對象,所以最好不要使用這個方式。另外,如果非要調用也應當調用編譯器推薦的,如 _endThread 【 Windows 核心編程 P127 】) l 同進程內的其他線程(包括主線程)調用 TerminateThread (被撤銷線程得不到通知,不能釋放資源,盡量避免這種方式。另外這個函數(shù)是個異步函數(shù),返回時,線程不保證已經被撤銷,如果要觀察線程是否被撤銷,應當使用 WaitForSingleObject ) l 包含線程的進程終止(應當避免這種方式) 4 .2 線程退出時 OS 的行為l 線程內的所有用戶對象被釋放。 l 線程的退出碼從 STILL_ACTIVE 改為傳遞給 ExitThread 或 TerminateThread 的代碼 l 線程內核對象的狀態(tài)改為“已通知” l 如果線程為進程中的最后一個線程,則 OS 將進程當作已終止運行 l 線程內核對象的引用計數(shù)器減 1 (一旦線程終止了,其他引用改線程內核對象將不能夠處理改線程的句柄,但是可以通過調用 GetExitcodeThread 來檢查 hThread 代表的線程是否已經終止運行了。) 5 線程同步5 .1 線程同步的起因以及解決之道5 .1.1 共用資源型:多個線程需要訪問同一個資源的時候,為了保證資源不被破壞,需要線程對資源的訪問具有原子性。5 .1.2 依賴型:一個線程等待另外一個線程某件事情完成后才能執(zhí)行 _ 可以通過手動事件的方式互相通知。5 .2 線程同步種類細分
特別說明: WaitForSingleObject/WaitForMultipleObject 是抑制線程本身的一種手法,配合以共用資源對象或所依賴的其他對象“通知狀態(tài)”的原子性變化,以達到線程在爭用資源、互相依賴時執(zhí)行的順序化,從而達到同步的目的。 綜上:其實 Windows 的線程同步機制是提供了一組不同情況下的資源爭用處理辦法而已。與此同時推出的Wait ××卻可以帶來很多其他好處,甚至部分緩解 C++ 語言沒有事件機制的缺憾,部分達到了 JAVA,C# 中事件機制的效果,為 Oberserve 模式的實現(xiàn)做了些貢獻。 |
|