線程由兩部分組成:線程核心對象和線程棧。關於核心對象請看windows核心對象簡介。
線程核心對象,作業系統用線程核心對象來管理線程,作業系統還用它來存放統計資訊。
線程棧,用於維護線程執行時所需的所有函數參數和局部變數,就是C#程式員常說的線程棧和託管堆中的線程棧。
我們知道進程是有惰性的,它的所有工作都是由線程完成的,而進程只是為線程提供場地,線程函數執行我們讓它執行的任務,最終線程函數將終止運行並返回,線程將終止運行,線程的記憶體將被釋放,線程核心對象的使用計數將減一,如果線程核心對象的使用計數減為0,線程核心對象將被銷毀。
說了這麼多,感覺有點虛,我們還是來看看建立線程的函數吧。
CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
PDWORD lpThreadId
);
lpThreadAttributes:用於設定線程的安全性,線程核心對象是否可以被進程的子進程所繼承,詳細瞭解我的這篇文章windows核心對象簡介。
dwStackSize:用於設定線程棧的大小,預設為1MB
lpStartAddress:線程所執行的函數的地址
lpParameter:線程函數的參數
dwCreationFlags:用於標識線程是否馬上執行
lpThreadId:線程ID
建立線程。調用CreateThread時,系統會建立一個線程核心對象,這個核心對象由作業系統管理,當它的使用計數為0時,會被自動銷毀。而當前進程的控制代碼表也會有一項用於紀錄線程核心對象,表示對它的一個引用,同時系統會在進程的地址空間中分配一塊記憶體共線程棧使用。新線程可以訪問進程控制代碼表中的所有核心對象,核心對象由系統核心管理,而進程控制代碼表維護該進程所用到的核心對象的引用,而線程則通過控制代碼表中核心對象的地址引用核心對象。新線程還可以訪問進程的所有記憶體以及進程中所有線程的棧,如子線程可以很方便的訪問主線程的棧。還有一點需要說明的就是,線程核心對象建立完成之後,線程並不會馬上執行,因為線程棧的記憶體配置和初始化時要時間的,等一切就緒後進程才會開始執行。
終止線程。最好讓線程函數自動返回,而不應該強制終止線程函數,因為線程函數在返回前還要做些清理工作,如析構對象,回收記憶體,讓作業系統正確釋放線程棧使用的記憶體,如果強制終止關閉線程,可能這些工作就不能正確的執行,很可能就會出現記憶體泄露。
線程內幕。對CreateThread的調用產生一個線程核心對象,核心對象的使用計數為2,個人認為使用計數為2的原因是:當前進程對它的引用,還有就是保證線上程函數返回是核心對象不用馬上自動銷毀,因為當前函數返回時核心對象的使用計數肯定要減一,當減為0時核心對象將自動銷毀。還要初始化核心對象的其他屬性,並將核心對象設為未觸發狀態,使其隨時可以被執行。等核心對象和線程棧記憶體一切就緒,系統就會將兩個值寫入新線程棧的最上端,一個是線程函數的參數,一個是線程函數的地址。
每個線程都有自己的一組CPU寄存器,即為線程的上下文。上下文反映了當前線程執行一次時線程CPU寄存器的狀態。因為一個線程可能要經過多次的CPU輪詢,才能執行完成,在每次CPU時間用完之前儲存CPU寄存器的狀態,好讓下次輪詢到達時繼續執行。線程CPU寄存器全部都儲存在一個CONTEXT結構中,而CONTEXT本身儲存線上程核心對象中。指令指標寄存器和棧指標寄存器是線程上下文中兩個重要的寄存器,線程始終線上程的上下文中運行,當線程的核心對象被初始化時,CONTEXT結構的堆棧指標寄存器被設為函數線上程棧中的地址,而指令指標寄存器被設為RtlUserThreadStart函數的地址,而函數RtlUserThreadStart則是線程開始的地方。
最後想說的是,建議用_beginthreadex代替CreateThread建立函數。
作者:陳太漢
部落格:http://www.cnblogs.com/hlxs/