核心编程6——线程
Note 0:
了進程實際是由兩個組件組成的:一個進程內(nèi)核對象和一個地址空間.類似地,線程也由兩個組件組成:
一個是線程的內(nèi)核對象,操作系統(tǒng)用它管理線程.內(nèi)核對象還是系統(tǒng)用來存放線程統(tǒng)計信息的地方.
一個線程堆棧,用于維護線程執(zhí)行時所需的所有函數(shù)參數(shù)和局部變量.
Note 1:
進程是有惰性的.進程從來不執(zhí)行任何東西,它只是一個線程的容器.線程必然是在某個進程的上下文中創(chuàng)建的,而且會在這個進程內(nèi)部"終其一生".這意味著線程要在其進程的地址空間內(nèi)執(zhí)行代碼和處理數(shù)據(jù).此外,這些線程還共享內(nèi)核對象句柄,因為句柄表是針對每一個進程的,而不是針對每一個線程.相較于線程,進程所使用的系統(tǒng)資源更多.其原因在于地址空間.為一個進程創(chuàng)建一個虛擬的地址空間需要大量系統(tǒng)資源.系統(tǒng)中會發(fā)生大量的記錄活動,而這需要用到大量內(nèi)存.而且,由于.exe和.dll文件要加載到一個地址空間,所以還需要用到文件資源.另一方面,線程使用的系統(tǒng)資源要少得多.事實上,線程只有一個內(nèi)核對象和一個堆棧;幾乎不涉及記錄活動,所以不需要占用多少內(nèi)存.
Note 2:
線程描述了進程內(nèi)部的一個執(zhí)行路徑.每次初始化進程時,系統(tǒng)都會創(chuàng)建一個主線程.對于用Microsoft C/C++編譯器生成的應(yīng)用程序,這個線程首先會執(zhí)行C/C++運行庫的啟動代碼,后者調(diào)用入口函數(shù)(_tmain或_tWinMain),并繼續(xù)執(zhí)行,直至入口函數(shù)返回C/C++運行庫的啟動代碼,后者最終將調(diào)用ExitProcess.對于許多應(yīng)用程序來說,這個主線程是應(yīng)用程序惟一需要的線程.但是,進程也可以創(chuàng)建額外的線程來幫助它們完成自己的工作.
Note 3:
操作系統(tǒng)的Windows Indexing Services(Windows索引服務(wù))創(chuàng)建了一個低優(yōu)先級的線程.此線程定期醒來.并對硬盤上的特定區(qū)域的文件內(nèi)容進行索引.Windows索引服務(wù)極大改進了性能.因為一旦成功建立索引.就不必在每次搜索時都打開、掃描和關(guān)閉硬盤上的每一個文件.配合這種索引服務(wù).Microsoft Windows Vista提供了一套高級的搜索功能.
Note 4:
如果想創(chuàng)建一個或
多個輔助線程.只需讓一個正在運行的線程調(diào)用CreateThread:
HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
DWORD cbStackSize,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD dwCreateFlags,
PDWORD pdwThreadID);
調(diào)用CreateThread時.系統(tǒng)會創(chuàng)建一個線程內(nèi)核對象.這個線程內(nèi)核對象不是線程本身.而是一個較小的數(shù)據(jù)結(jié)構(gòu).操作系統(tǒng)用這個結(jié)構(gòu)來管理線程.可以把線程內(nèi)核對象想象為一個由線程統(tǒng)計信息構(gòu)成的小型數(shù)據(jù)結(jié)構(gòu).這與進程和進程內(nèi)核對象之間的關(guān)系是相同的
系統(tǒng)將進程地址空間的內(nèi)存分配給線程堆棧使用.新線程在與負責(zé)創(chuàng)建的那個線程相同的進程上下文中運行.因此.新線程可以訪問進程內(nèi)核對象的所有句柄、進程中的所有內(nèi)存以及同一個進程中其他所有線程的堆棧.這樣一來.同一個進程中的多個進程可以很容易地互相通信.
Note 5:
CreateThread函數(shù)是用于創(chuàng)建線程的Windows函數(shù).不過.如果寫的是C/C++代碼.就絕對不要調(diào)用CreateThread.相反.正確的選擇是使用Microsoft C++運行庫函數(shù)_beginthreadex.如果使用的不是Microsoft C++編譯器.你的編譯器的提供商應(yīng)該提供類似的函數(shù)來替代CreateThread.不管這個替代函數(shù)是什么.都必須使用它.
Note 6:
使用鏈接器的/STACK開關(guān)來控制線程堆棧使用多少地址空間.如下所示:
/STACK:[reserve] [,commit]
reserve參數(shù)用于設(shè)置系統(tǒng)將為線程堆棧預(yù)留多少地址空間.默認是1 MB(在Itanium芯片組上.默認大小為 4 MB).commit參數(shù)指定最初應(yīng)為堆棧的保留區(qū)域提交多少物理存儲空間.默認是1頁.隨著線程中的代碼開始執(zhí)行.需要的存儲空間可能不止1頁.如果線程溢出它的堆棧.會產(chǎn)生異常.(有關(guān)線程堆棧和堆棧溢出異常的詳情.請參見第16章.有關(guān)常見異常處理的詳情.請參見第23章.)系統(tǒng)將捕獲這種異常.并將另一個頁(或者為commit參數(shù)指定的任何大小)提交給保留空間.這樣一來.線程堆棧就可以根據(jù)需要動態(tài)地增大.
Note 7:
保留空間的容量設(shè)定了堆棧空間的上限.這樣才能捕獲代碼中的無窮遞歸bug.例如.假設(shè)你寫了一個函數(shù)以遞歸方式調(diào)用其自身.而且這個函數(shù)存在一個bug.會導(dǎo)致無窮遞歸.每次此函數(shù)調(diào)用自身時.都會在內(nèi)存堆棧上創(chuàng)建一個新的堆棧幀.如果系統(tǒng)沒有設(shè)定堆棧空間的上限.這個遞歸調(diào)用的函數(shù)就永遠不會終止調(diào)用自身.進程的所有地址空間都會被分配出去.大量物理存儲會提交給堆棧.通過設(shè)置堆棧空間的上限.可以防止應(yīng)用程序耗盡物理內(nèi)存區(qū)域.而且還可以盡早察覺程序中的bug.
Note 8:
創(chuàng)建多個線程時.可以讓它們使用同一個函數(shù)地址作為起點.這樣做完全合法.而且非常有用.(可以傳遞了不同的pvParam值,來區(qū)分不同的線程所調(diào)用相同的函數(shù).)
Note 9:
TerminateThread函數(shù)是異步的.也就是說.它告訴系統(tǒng)你想終止線程.但在函數(shù)返回時.并不保證線程已經(jīng)終止了.如果需要確定線程已終止運行.還需要調(diào)用WaitForSingleObject或類似的函數(shù).并向其傳遞線程的句柄.
Note 10:
如果通過返回或調(diào)用ExitThread函數(shù)的方式來終止一個線程的運行.該線程的堆棧也會被銷毀.但是.如果使用的是TerminateThread.那么除非擁有此線程的進程終止運行.否則系統(tǒng)不會銷毀這個線程的堆棧.Microsoft故意以這種方式來實現(xiàn)TerminateThread.否則.假如其他還在運行的線程要引用被"殺死"的那個線程的堆棧上的值.就會引起訪問沖突.讓被"殺死"的線程的堆棧保留在內(nèi)存中.其他的線程就可以繼續(xù)正常運行.
此外.動態(tài)鏈接庫(DLL)通常會在線程終止運行時收到通知.不過.如果線程是用TerminateThread強行"殺死"的.則DLL不會收到這個通知.其結(jié)果是不能執(zhí)行正常的清理工作.
Note 11:
線程終止運行時.會發(fā)生下面這些事情:
線程擁有的所有用戶對象句柄會被釋放.在Windows中.大多數(shù)對象都是由包含了"創(chuàng)建這些對象的線程"的進程擁有的.但是.一個線程有兩個User對象:窗口(window)和掛鉤(hook).一個線程終止運行時.系統(tǒng)會自動銷毀由線程創(chuàng)建或安裝的任何窗口.并卸載由線程創(chuàng)建或安裝的任何掛鉤.其他對象只有在擁有線程的進程終止時才被銷毀.
線程的退出代碼從STILL_ACTIVE變成傳給ExitThread或TerminateThread的代碼.
線程內(nèi)核對象的狀態(tài)變?yōu)閟ignaled.
如果線程是進程中的最后一個活動線程.系統(tǒng)認為進程也終止了.
線程內(nèi)核對象的使用計數(shù)遞減1.
Note 19:
對CreateThread函數(shù)的一個調(diào)用導(dǎo)致系統(tǒng)創(chuàng)建一個線程內(nèi)核對象.該對象最初的使用計數(shù)為2.(除非線程終止,而且從CreateThread返回的句柄關(guān)閉,否則線程內(nèi)核對象不會被銷毀.)該線程內(nèi)核對象的其他屬性也被初始化:暫停計數(shù)被設(shè)為1,退出代碼被設(shè)為STILL_ACTIVE (0x103),而且對象被設(shè)為nonsignaled狀態(tài).
Note 20:
一旦創(chuàng)建了內(nèi)核對象,系統(tǒng)就分配內(nèi)存,供線程的堆棧使用.此內(nèi)存是從進程的地址空間內(nèi)分配的,因為線程沒有自己的地址空間.然后,系統(tǒng)將兩個值寫入新線程堆棧的最上端.(線程堆棧始終是從高位內(nèi)存地址向低位內(nèi)存地址構(gòu)建的.)寫入線程堆棧的第一個值是傳給CreateThread函數(shù)的pvParam參數(shù)的值.緊接在它下方的是傳給CreateThread函數(shù)的pfnStartAddr值.
Note 21:
每個線程都有其自己的一組CPU寄存器,稱為線程的上下文(context).上下文反映了當(dāng)線程上一次執(zhí)行時,線程的CPU寄存器的狀態(tài).線程的CPU寄存器全部保存在一個CONTEXT結(jié)構(gòu)(在WinNT.h頭文件中定義).CONTEXT結(jié)構(gòu)本身保存在線程內(nèi)核對象中.
Note 22:
當(dāng)線程的內(nèi)核對象被初始化的時候,CONTEXT結(jié)構(gòu)的堆棧指針寄存器被設(shè)為pfnStartAddr在線程堆棧中的地址.而指令指針寄存器被設(shè)為RtlUserThreadStart函數(shù)(該函數(shù)未見于正式文檔)的地址,此函數(shù)是NTDLL.dll模塊導(dǎo)出的.
RtlUserThreadStart函數(shù)的基本用法如下:
VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {
__try {
ExitThread((pfnStartAddr)(pvParam));
}
__except(UnhandledExceptionFilter(GetExceptionInformation())) {
ExitProcess(GetExceptionCode());
}
// NOTE: We never get here.
}
Note 23:
線程完全初始化好之后,系統(tǒng)將檢查CREATE_SUSPENDED標(biāo)志是否傳給CreateThread函數(shù).如果此標(biāo)記沒有傳遞,系統(tǒng)將線程的暫停計數(shù)遞增至0;隨后,線程就可以調(diào)度給一個處理器去執(zhí)行.然后,系統(tǒng)在實際的CPU寄存器中加載上一次在線程上下文中保存的值.現(xiàn)在,線程可以在其進程的地址空間中執(zhí)行代碼并處理數(shù)據(jù)了.
Note 24:
新線程執(zhí)行RtlUserThreadStart函數(shù)的時候,將發(fā)生以下事情:
圍繞你的線程函數(shù),會設(shè)置一個結(jié)構(gòu)化異常處理(Structured Exception Handling,SEH)幀.這樣一來,線程執(zhí)行期間所產(chǎn)生的任何異常都能得到系統(tǒng)的默認處理.
系統(tǒng)調(diào)用你的線程函數(shù),把你傳給CreateThread函數(shù)的pvParam參數(shù)傳給它.
線程函數(shù)返回時,RtlUserThreadStart調(diào)用ExitThread,將你的線程函數(shù)的返回值傳給它.線程內(nèi)核對象的使用計數(shù)遞減,而后線程停止執(zhí)行.
如果線程產(chǎn)生了一個未被處理的異常,RtlUserThreadStart函數(shù)所設(shè)置的SEH幀會處理這個異常.通常,這意味著會向用戶顯示一個消息框,而且當(dāng)用戶關(guān)閉此消息框時,RtlUserThreadStart會調(diào)用ExitProcess來終止整個進程,而不只是終止有問題的線程.
Note 25:
當(dāng)RtlUserThreadStart開始執(zhí)行時,它會調(diào)用C/C++運行庫的啟動代碼,后者初始化繼而調(diào)用你的_tmain或_tWinMain函數(shù).你的入口函數(shù)返回時,C/C++運行時啟動代碼會調(diào)用ExitProcess.所以對于C/C++應(yīng)用程序來說,主線程永遠不會返回到RtlUserThreadStart函數(shù).
Note 26:
Visual Studio附帶了4個原生的C/C++運行庫,還有2個庫面向Microsoft.NET的托管環(huán)境.注意,所有這些庫都支持多線程開發(fā):不再有單獨的一個C/C++庫專門針對單線程開發(fā).下面對這些庫進行了描述.
Microsoft Visual Studio附帶的C/C++庫
庫名稱????? 描述
LibCMt.lib?? 庫的靜態(tài)鏈接Release版本
LibCMtD.lib 庫的靜態(tài)鏈接Debug版本
MSVCRt.lib?? 導(dǎo)入庫,用于動態(tài)鏈接MSVCR80.dll 庫的Release版本. (這是新建項目時的默認庫)
MSVCRtD.lib 導(dǎo)入庫,用于動態(tài)鏈接MSVCR80D.dll庫的Debug版本
MSVCMRt.lib 導(dǎo)入庫,用于托管/原生代碼混合
MSVCURt.lib 導(dǎo)入庫,編譯成百分之百純MSIL代碼
-------------------------------------------------------------------
?
?轉(zhuǎn)載于:https://www.cnblogs.com/lzjsky/archive/2010/06/04/1751719.html
總結(jié)
- 上一篇: GTK+ 2.4 or later is
- 下一篇: 【Android -- 数据存储】Lit