




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
理解線程是非常關鍵的,因為每個進程至少需要一個線程。本章將更加詳細地介紹線程的知識。尤其是要講述進線程之間存在多大的差別,它們各自具有什么作用。還要介紹系統如何使用線程內核對象來管理線程。與進程內核對象一樣,線程內核對象也擁有屬性,要觀察許多用于查詢和修改這些屬性的函數。此外還要介紹可以在進程中創建和生成的線一個是線程的內核對象,操作系統用它來對線程實施管理。內核對象也是系統用來存放線程統計信息的地方。第4章中講過,進程是不活潑的。進程從來不執行任何東西,它只是線程的容器。線程總是在某個進程環境中創建的,而且它的整個期都在該進程中。這意味著線程在它的進程地址空間中執行代碼,并且在進程的地址空間中對數據進行操作。因此,如果在單進程環境中,你有兩個或多個線程正在運行,那么這兩個線程將共享單個地址空間。這些線程能夠執行相同的代碼,對相同的數據進行操作。這些線程還能共享內核對象句柄,因為句柄表依賴于每個進程而不是每個線程存在。如你所見,進程使用的系統資源比線程多得多,原因是它需要的地址空間。為進程創建一個虛擬地址空間需要許多系統資源。系統中要保留大量的記錄,這要占用大量的內存。另外,由于.exe和.dl文件要加載到一個地址空間,因此也需要文件資源。而線程使用的系統資源要少得多。實際上,線程只有一個內核對象和一個堆棧,保留的記錄很少,因此需要很少的內存。由于線程需要的開銷比進程少,因此始終都應該設法用增加線程來解決編程問題,而要避免創建新的進程。但是,這個建議并不是一成不變的。許多程序設計用多個進程來實現會更好些。應該懂得權衡利弊,經驗會指導你的編程實踐。線程用于描述進程中的運行路徑。每當進程被初始化時,系統就要創建一個主線程。該線C/C++運行期庫的啟動代碼一道開始運行,啟動代碼則調用進入點函數(main、wmain、inMain或wWinMain且C/C++運行期庫的啟動代碼調用ExitProcess為止。對于許多應用程序來說,這個主線程是應用程序需要的唯一線程。不過,進程能夠創建 的線程來幫助執行它們的操作。每個計算機都擁有一個功能非常強大的資源,即CPU。讓CPU可以打開 Windows2000配備的內容索引服務程序。它能夠創建一個低優先級的線程,以便定期打開你的磁盤驅動器上的文件內容并給內容做索引。若要找到一個文件,可以打開SearchResult(搜索結果)窗口(擊Start按鈕,從Search菜單中選定ForFilesOrFolders,再你的搜條輸入Containingext。時就可搜到索引,相關的文件就會立即顯示出來。內容索引服務程序大大改進了性能,因為每次搜索不必打開、掃描和關閉磁盤驅動器上的每個文件。可以使用Windows2000配備的磁盤碎片整理軟件。通常情況下,這種類型的實用程序擁有許多管理選項,一般用戶可能不懂,比如該實用程序應該相隔多長時間運行一次,何時運行。使用低優先級線程,可以在運行該實用程序,并且在系統空閑時對驅動器可以很容易地設想將來版本的編譯器,每當暫停鍵入時,它就可以自動編譯你的源代碼文件。輸出窗口可以向你(幾乎)實時顯示警告和出錯信息。當鍵入變量和函數名時出VisualStudio已經實現了這個功能,使用orkspace的ClassView窗格,就能夠看到這些信息。Web瀏覽器在與它們的服務器進行通信。因此,在來自當前Web站點的結果輸入之這些例子中,有一個重要問題應該注意,那就是多線程能夠簡化應用程序的用戶界面。如果每當停止鍵入時,編譯器建立了你的應用程序,那么就沒有必要提供Build菜單選項。文字處理應用程序不需要CheckSpelling(拼寫檢查)和CheckGrr(語法檢查)菜單選項。在WebI/O(網絡、文件或其他),應用程序的用戶界面就能夠始終保持工作狀態。比一個應用程序負責給數據庫記錄進行排序、打印文檔或拷貝文件。如果將獨立的線程用于處理這個與I/O相關的任務,用戶就可以在進程中繼續每個線程被分配了一個CPU。因此,如果你的計算機擁有兩個CPU,你的應用程序中有兩個線程,那么兩個CPU都將處于繁忙狀態。實際上,你是讓兩個任務在執行一個任務的時間內完成操作。每個進程至少擁有一個線程。因此,如果你在應用程序中不執行任何特殊的操作,在多進程操作系統上運行,就能夠得到許多好處。例如,可以建立一個應用程序,并同時使用文字處理程序(我常常這樣做CPU,那么該應用程序就可以在一個處理器上執行,而另一個處理器則負責處理文檔。另外,如果編譯器出現一個錯誤,導致它的線程進入一個無限循環,仍然可以使用其他的進程(16位indows和MS-DOS。也存在某些不足之處。有些開發人員認為,解決問題的方法是將它分割成多個線程。這種想法例如,你開發了一個文字處理應用程序,并且想要讓打印函數作為它自己的線程來運行。這聽起來是個很好的主意,因為用戶可以在打印文檔時立即回頭著手編輯文檔。但是,這意味著文檔中的數據可能在文檔打印時變更。也許最好是不要讓打印操作在它自己的線程中發生,不過這種“方案”看起來有點兒。如果你讓用戶編輯另一個文檔,但是鎖定正在打印的文檔,使得打印結束前該文檔不能修改,那將會怎樣呢?這里還有第三種思路,將文檔拷貝到一個臨時文件,然后打印該臨時文件的內容,并讓用戶修改原始文檔。當包含該文檔的臨時文件結束打印時,刪除臨時文件。如你所見,線程能夠解決某些問題,但是卻又會產生新的問題。在開發應用程序的用戶界面時,很可能出現對線程的另一種誤用。幾乎在所有的應用程序中,所有用戶界面的組件(窗口)應該共享同一個線程。單個線程應該創建窗口的所有子窗口。有時在不同的線程上創建不同的窗口是有用的,不過這種情況確實非常少見。通常情況下,一個應用程序擁有一個用戶界面線程,用于創建所有窗口,并且有一個GetMessage循環。進程中的所有其他線程都是工作線程,它們與計算機或I/O相關聯,但是這些線程從不創建窗口。另外,一個用戶界面線程通常擁有比工作線程更高的優先級,因此用戶界面負責向用戶作出響應。WindowsExplorer為每個文件夾窗口創建了一個獨立的線程。它使你能夠將文件從一個文件夾拷貝到另一個文件夾,并且仍然可以查看你的系統上的其他文件夾。另外,如果Explorer中存在一個錯誤,那么負責處理文件夾的線程可能,但是仍然能夠對其他文件夾進行操作,至少在執行的操作導致其他文件夾也之前,仍然可以對它們進行操作(關于線程和用戶界面的詳細說明,參見第2627章。上述內容的實質是應該慎重地使用多線程。不要想用就用。僅僅使用賦予進程的主線程,就能夠編寫出許多非常有用的和功能強大的應用程序。每個線程必須擁有一個進入點函數,線程從這個進入點開始運行。前面已經介紹了主線程的進入點函數:即main、wmain、inMain或wWinMain。如果想要在你的進程中創建一個輔助線程,它必定也是個進入點函數,類似下面的樣子:這時,線程終止運行,該堆棧的內存被釋放,同時,線程的內核對象的使用計數被遞減。如果使用計數降為0,線程的內核對象就被撤消。與進程內核對象的情況相同,線程內核對象的至少可達它們相聯線程那長不過,對的以遠超過程身的。由于給你的主線程的進入點函數傳遞了字符串參數,因此可以使用ANSI/Unicode版本的進入點函數:main/wmain和WinMain/wWinMain。可以給線程函數傳遞單個參數,參數線程函數必須返回一個值,它將成為該線程的退出代碼。這與C/C++運行期庫關于讓主線程函數(實際上是你的所有函數)應該盡可能使用函數參數和局部變量。當使用靜態變量和全局變量時,多個線程可以同時這些變量,這可能破壞變量的內容。然而,參數和局部變量是程堆棧中創建的,因此它們不太可能被另一個線程破壞。前面已經講述了調用CreateProcess函數時如何創建進程的主線程。如果想要創建一個或多個輔助函數,只需要讓一個已經在運行的線程來調用CreateThread:當CreateThread被調用時,系統創建一個線程內核對象。該線程內核對象不是線程本身,而是操作系統用來管理線程的較小的數據結構。可以將線程內核對象視為由關于線程的統計信息組成的一個小型數據結構。這與進程和進程內核對象之間的關系是相同的。注意CreateThread函數是用來創建線程的WindowsC/C++代碼,決不應該調用CreateThread。相反,應該使用VisualC++_beginthreade。如果不使用 VisualC++編譯器,你的編譯器供應商有它自己的CreateThred替代函數。不管這個替代函數是什么,你都必須使用。本章后面將要介紹_beginthreade能夠做什么,它的重要性何在。這就是CreateThread函數的概述,下面各節將要具體介紹CreateThreadpsa參數是指向SECURITY_TTRIBUTES結構的指針。如果想要該線程內核對象的默認安全屬性,可以(并且通常能夠)傳遞NULL。如果希望所有的子進程能夠繼承該線程對象的句柄,必須設定一個SECURITY_TTRIBUTES結構,它的bInheritHandle成員被初始化為TRU。詳細信息參見第3章。cbStack參數用于設定線程可以將多少地址空間用于它自己的堆棧。每個線程擁有它自己的堆棧。當CreateProcessCreateThread來對進程的主線reserve參數用于設定系統應該為線程堆棧保留的地址空間量。默認值是1。mit參數用于設定開始時應該承諾用于堆棧保留區的物理器的容量。默認值是1頁。當線程中的代碼執行時,可能需要多個頁面的器。當線程溢出它的堆棧時,就生成一個異常條件(關于線程堆棧和堆棧溢出的異常條件的詳細說明,參見第16章,關于一般異常條件的處理的詳細說明,參見第23章。系統抓取該異常條件,并且將另一頁(或者你為commit參數設定的任何當調用CreateThrea時,如果傳遞的值不是0,就能使該函數將所有的器保留并分配給線程的堆棧。由于所有的器預先作了分配,因此可以確保線程擁有指定容量的可用堆棧存儲器。保留空間的容量既可以是/SACK程序設定的容量,也可以是CbStack的值,大就用誰。分配的器容量應該與傳遞的cbStack值相一致。如果將0傳遞給CbStack參數,CreateThread就保留一個區域,并且將程序嵌入.ex文件的/SACK程序開關信息指明的器容量分配給線程堆棧。pfnStartAddpvParam原先傳遞給CreateThread的pvParam參數是相同的。CreateThread使用該參數不做別的事情,只是程啟動執行時將該參數傳遞給線程函數。該參數提供了一個將初始化值傳遞給線程函數的。該初始化數據既可以是數字值,也可以是指向包含其他信息的一個數據結構的指針。記住,Windows是個搶占式多線程系統,這意味著新線程和調用CreateThread的線程可以FirstThrea可以在SecondThrea將分配給FirstThrea的x操作。如果出現這種情況,SecondThread將不知道FirstThread現在已經是個無效地址的內容。這會導致SecondThrea產生一次,因為FirstThread的堆棧已經在FirstThread將x為一個靜態變量建一個區而是在堆上建。因為兩個線程將共享該靜態變量。解決這個問題(和它的更復雜的變形)的另法是使用正確的線程同步技術(第8、9章和10。fdwCreate參數可以設定用于控制創建線程的其他標志。它可以是兩個值中的一個。如果該值是0,那么線程創建后可以立即進行調度。如果該值是CREATE_SUSPENDED,系統可以完整地創建線程并對它進行初始化,但是要暫停該線程的運行,這樣它就無法進行調度。CREATE_SUSPENDE標志使得應用程序能夠在它有機會執行任何代碼之前修改線程的某些屬性。由于這種必要性很少,因此該標志并不常用。第5章介紹的JobLab應用程序說明了該標志的正確方法。注意在Windows2000(和WindowsNT4)下,可以(并且通常是這樣做的)為該參數傳遞NULL。它告訴函數,你對線程的ID不感,但是線程已經創建了。在Windows95和Windows98下,為該參數傳遞NULL會導致函數運行失敗,因為函數試個應用程序,當后來在Windows98上運行該應用程序時,CreateThread將不創建新的線該函數將終止線程的運行,并導致操作系統清除該線程使用的所有操作系統資源。但是,C++資源(如C++類對象)將不被撤消。由于這個原因,最好從線程函數返回,而不是通過調用ExitThread來返回(詳細說明參見第4章。當然,可以使用ExitThread的dwExitThread參數告訴系統將線程的退出代碼設置為什么。注意 終止線程運行的最佳方法是讓它的線程函數返回。但是,如果使用本節介紹的方法應該道ExitThread函數是indow用來消程的函。果編C/C++代碼,那么決不應該調用ExitThread。應該用VisualC++運行期庫數_endthreadex如果不用 的VisualC++編譯器,你的編譯器供應商有它自己的ExitThread的替代函數。不管這個替代函數是什么,都必須使用。本章后面將說明_endthreadex的作用和它重性。與ExitThreadExitThreaderminateThreadhThread參數用于標識被終止運行的線程的句柄。當線程終止運行時,它的退出代碼成為你作為dwExitCode參數傳遞的值。同時,線程的內核對象的使用計數也被遞減。erminateThread函數是異步運行的函數,也就是說,它告訴系統你想要線程終止運行,但是,當函數返回時,不能保證線程被撤消。如果需要確切地知道該線程已經終止運行,必須調用aitForSingleObject(第9章介紹)或者類似的函數,傳遞線程的句柄。 當使用返回或調用ExitThread的方法撤消線程時,該線程的內存堆棧也被撤消。 DLL通常接收通知。如果使用TerminateThread強迫線程終止,DLL就不接收通知,這能適當的清除(詳細信息參見第20章。第4章介紹的ExitProcess和erminateProcess函數也可以用來終止線程的運行。差別在于這些線程將會使終止運行的進程中的所有線程全部終止運行。另外,由于整個進程已經被關閉,進程使用的所有資源肯定已被清除。這當然包括所有線程的堆棧。這兩個函數會導致進程中的剩余線程被強制撤消,就像從每個剩余的線程調用erminateThread一樣。顯然,這意味著正確的應用程序清除沒有發生,即C++對象撤消函數沒有被調用,數據沒有轉至磁盤等等。線程擁有的所有用戶對象均被釋放。在indows中,大多數對象是由包含創建這些對象的線程的進程擁有的。但是一個線程擁有兩個用戶對象,即窗口和掛鉤。當線程終止運行時,系統會自動撤消任何窗口,并且卸載線程創建的或安裝的任何掛鉤。其他對象只有在擁有線程的進程終止運行時才被撤消。線程的退出代碼從STILL_ACTIVE改為傳遞給ExitThread或TerminateThread線程內核對象的使用計數遞減1當一個線程終止運行時,在與它相關聯的線程內核對象的所有未結束的關閉之前,該一旦線程不再運行,系統中就沒有別的線程能夠處理該線程的句柄。然而別的線程可以調用GetExitcodeThread來檢查由hThread標識的線程是否已經終止運行。如果它已經終止運行,退出代碼的值在pdwExitCode指向的DWORD中返回。如果調用GetExitCodeThread時線程尚未終止運行,該函數就用STILL_ACTIVE標識符(定義為0x103)填入DWORD。如果該函數運行成功,便返回TRUE(第9章將詳細地介紹如何使用線程的句柄來確定何時線程終止運行。圖6-1顯示了系統在創建線程和對線行初始化時必須做些什么工作。讓我們仔細看一看這個圖,以便確切地了解發生的具體情況。調用CreateThread可使系統創建一個線程內核對象。該對象的初始使用計數是2(程停止運行和從CreateThread返回的句柄關閉之前,線程內核對象不會被撤消1,退出代碼始終為STILL_ACTIV(0x103,該對象設置為未通知狀態。圖6-1一旦內核對象創建完成,系統就分配用于線程的堆棧的內存。該內存是從進程的地址空間分配而來的,因為線程并不擁有它自己的地址空間。然后系統將兩個值寫入新線程的堆棧的上端(線程堆棧總是從內存的高地址向低地址建立。寫入堆棧的第一個值是傳遞給CreateThread的pvParam參數的值。緊靠它的下面是傳遞給CreateThrea的pfnStartAddr每個線程都有它自己的一組CPU寄存器,稱為線程的上下文。該上下文反映了線程上次運行時該線程的CPU寄存器的狀態。線程的這組CPU寄存器保存在一個CONTEXT結構(在WiNT.h件中作定)中。CNEXT構本則包含的內核象。指令指針和堆棧指針寄存器是線程上下文中兩個最重要的寄存器。記住,線程總是在進程的上下文中運行的。因此,這些地址都用于標識擁有線程的進程地址空間中的內存。當線程的CONTEX結構的堆棧指針寄存器被設置為線程堆棧上用來放置pfnStart-Addr的地址。指令指針寄存器置為稱為BaseThreadStart的未文檔化(和未輸出)的函數的地址中。該函數包含在Kernel32.dl模塊中(這也是實現CreateThread函數的地方。圖6-當線程完全初始化后,系統就要查看CREATE_SUSPENDED標志是否已經傳遞給CreateThread0,該線程可以調度到一個進程中。然后系統用上次保存程上下文中的值加載到實際的CPU寄存器中。這時線程就由于新線程的指令指針被置為BaseThreadStarBaseThreadStart的原型會使你認為該函數接收了兩個參數,但是這表示該函數是由另一個函數BaseThreadStart認為它是由另一個函數調用的,因為它可以兩個函數。但是,之所以可以這些參數,是因為操作系統將值顯式寫入了線程的堆棧(這就是參數通常傳遞給函數的方法。注意,有些CPU結構使用CPU寄存器而不是堆棧來傳遞參數。對于這些結構來說,系統將在允許線程執行BaseThreadStart函數之前對相應的寄存器正確地進行初始化。當線程函數返回時,BaseThreadStart調用ExitThread,并將線程函數的返回值傳遞給它。如果線程產生一個沒有處理的異常條件,由BaseThreadStart函數建立的SEHBsehredStt調用Exithrad注意,在BsThredStrt函數中,線程要么調用xitThred,要么調用xitPocss。這意味著線程不能退出該函數,它總是在函數中被撤消。這就是BaseThreadStart的原型規定返回另外,由于使用BaseThreadStart,線程函數可以在它完成處理后返回。當BaseThreadStartBaseThreadStart,因為線程堆棧上不存在返回地址,并且BaseThreadStart 開始執行時,它調用 開始執行時,它調用C/C++運行期庫的啟動代碼,該啟動代碼先要初始化main、wmain、WinMain或wWinMain函數,然后調用這些函數。當進入點函數返回時,C/C++運行期庫的啟動代碼就調用ExitProcess。因此,對于C/C++應用程序來說,主線程從不返回BaseProcessStart函數。 表6-1C/C++ 用于動態MSVCRtD.dll的調試版的輸入庫。該庫同時支持單線程應用程當實現任何類型的編程項目時,必須知道將哪個庫與你的項目相。可以使用圖6-2所示的ProjectSettings框來選定一個庫。在C/C++選項卡上,在CodeGeneration(生成的代碼)類別中,從Userun-timelibrary(使用運行期庫)組合框中選定6個選項中的一個。圖6-2ProjectSettingsC運行期庫是1970年問世的,它遠遠早于線程在任何應用程序上的應用。運行期庫的發明者沒有考慮到將運行期庫用于多線程應用程序的問題。考慮一下標準C運行期的全局變量rro。有些函數在發生錯誤時設置該變量。假設擁有下現在,假設在調用system函數之后和調用if語句之前,執行上面代碼的線程中斷運行,同時假設,該線程中斷運行是為了讓同一進程中的第二個線程開始執行,而這個新線程將執行另一個負責設置全局變量errno的CCPU在晚些時候重新分配給第一個線程時,errno的值將不再能夠反映調用上面代碼中的system函數時的錯誤代碼。為了解決這個問題,每個線程都需要它自己的errno變量。此外,必須有一種機制,使得線程能夠它自己的errno變量,但是又不觸及另一個線程的errno變量。這是標準C/C++運行期庫原先并不是設計用于多線程應用程序的唯一一個例子。在多線程環境中存在問題的C/C++運行期庫變量和函數包括errno_doserrnostrtok_wcstokstrerro、若要使多線程C和C++程序能夠正確地運行,必須創建一個數據結構,并將它與使用那么系統是否知道在創建新線程時分配該數據塊呢?回答是它不知道。系統根本不知道你得到的應用程序是用C/C++編寫的,也不知道你調用函數的線程本身是不安全的。問題在于你必須正確地進行所有的操作。若要創建一個新線程,絕對不要調用操作系統的CreateThread函數,必須調用C/C++運行期庫函數_beginthreadex:_beginthreadex函數的參數列表與CreateThread函數的參數列表是相同的,但是參數名和類型并不完全相同。這是因為的C/C++C/C++運行期函數不應該對Windows數據類型有任何依賴。_beginthreadex函數也像CreateThread那樣,返回新創建的線程的句柄。因此,如果調用源代碼中的CreateThread,就很容易用對_beginthreadex的調用全局取代所有這些調用。不過,由于數據類型并不完全相同,所以必須進行某種轉換,使編譯器運行得順利些。為了使操作更加容易,我在源代碼中創建了一個宏chBEGINTHREADEX:注意,_beginthreade函數只存在于C/C+運行期庫的多線程版本中。如果到單線程運行期庫,就會得到一個程序報告的“未轉換的外部符號”錯誤消息。當然,從設計上講,這個錯誤的原因是單線程庫在多線程應用程序中不能正確地運行。另外需要注意,當創建一個VisualStudio默認選定單線程庫。這并不是最安全的默認設置,對于多線程應用程序來說,必須顯式轉換到多線程的C/C++ 為C/C++運行期庫提供了源代碼,因此很容易準確地確定CreateThread究竟無法執行哪些_beginthreadex能執行的操作。實際上,我搜索了VisualStudio的光盤,發_beginthreadex的源代碼在Threadex.c中。代換重新打印它的源代碼,這里提供了它的偽代碼版本,并且列出它的一些令人感 的要點:于Mtdll.h文件中的VisualC++源代碼中。我在6-1中重建了它的結構。傳遞給_beginthreadex的線程函數的地址保存在tiddata內存塊中。傳遞給該函數的參數也保存在該數據塊中。_beginthreadex確實從內部調用CreateThread,因為這是操作系統了解如何創建新線程的唯一方法。當調用CreatetThread時,它知通過調用_threadstartex而不是pfnStartAddr新線程。還有,傳遞給線程函數的參數是tiddata結構而不是pvParam的地址。如果一切順利,就會像CreateThread6-1C/C++運行期庫的線程局部tiddata既然為新線程指定了tddta結構,并且對該結構進行了初始化,那么必須了解該結構與線程之間是如何關聯起來的。讓我們觀察一下_hreadstrtex函數(它也位于CC++運行期庫的新線程開始從BasethreadStart函數(在kernel32dll文件中)執行,然后轉移到到達該新線程的tiddata塊的地址作為其唯一參數被傳遞給_threadstartexTlsSetValue是個操作系統函數,負責將一個值與調用線程聯系起來。這稱為線程本地存儲器(TL,將在第21章介紹。_threadstartex函數將tiddata塊與線程聯系起來。—例如,運行期錯誤(比如放過了沒有抓住的C++異常條件)和C/C++運行期庫的signal函數。這是特別重要的。如果用CreateThread函數來創建線程,然后調用C/C++運調用必要的線程函數,傳遞必要的參數。記住,函數和參數的地址由_beginthreadex保存必要的線程函數返回值被認為是線程的退出代碼。注意,_threadstartex并不只是返回到BaseThreadStart。如果它準備這樣做,那么線程就終止運行,它的退出代碼將被正確地設置,但是線程的tiddata內存塊不會被撤消。這將導致應用程序中出現一個。若要防止這個,可以調用另一個C/C+運行期庫函數_endthreadex,傳遞退代。需要介紹的最后一個函數是_endthreadex(位于C運行期庫的Threadex.c文件中。下面是該函數的偽代碼版本:C運行期庫的_getptd函數內部調用操作系統的TlsGetValue函數,該函數負責檢索調用線程的tiddata面用ExitThread函數。這一點完全正確,我并不想收回我已經的話。ExitThread函數將撤消調用函數,并且不允許它從當前執行的函數返回。C++對象都不會被撤消。避免調用ExitThread原因是,它會使得線程的tiddat內存塊無法釋放,這樣,應用程序將會始終占用內存(直到整個進程終止運行為止。的VisualC++ExitThread,因此他們實現了他們_endthreadex(而不是調用ExitThread)以便釋放線程的tiddata塊,然后退出。不過建議不要調用_endthreadex現在應該懂得為什么C/C++運行期庫的函數需要為它創建的每個線程設置單獨的數據塊,_beginthreadex來分配數據塊,再對它進行初始化,將該數據得_endthreadex函數是如何程終止運行時釋放數據塊的。一旦數據塊被初始化并且與線程聯系起來,線程調用的任何需要單線程實例數據的C/C++運行期庫函數都能很容易地(通過TlsGetValue)檢索調用線程的數據塊地址,并對線程的數據進行操作。這對于函數來說很好,但是你可能想知道它對errno之類的全局變量效果如何。如果創建一個多線程應用程序,必須在編譯器令行上設定/MT(指多線程應用程序)或/MD(指多線程DLL)開關。這將使編譯器能夠定義_MT標識符。然后,每當errno時,實際上是調用內部的C/C++運行期庫函數_errnoerrnoerrno宏被定義為獲取該地址的內容的宏。這個定義是必要的,因為可以編寫類似下面形式的代碼:多線程版本的C/C++運行期庫還給某些函數設置了同步的基本要素。例如,如果兩個線程同時調用mallocC/C++運行期庫能夠防止兩個線程同時從堆棧中分配內存。為此,它要讓第二個線程等待,直到第一個線malloc返。然后第二個線程才被允許進入(8、9章和10。顯然,所有這些附加操作都會影響多線程版本的C/C++運行期庫的性能。這就是為什么公司除了多線程版本外,還提供單線程版本的靜態的C/C++運行期庫的原因。CCCC期庫函數的所有正在運行的應用程序和DLL共享。由于這個原因,運行期庫只存在于多線程版本中。由于LL中提供了/++運行期庫,因此應用程序(.xe文件)和DLL不需要包含CC運行期庫函數的代碼,結果它們的規模就比較小。另外,如果排除了CC++運行期庫正如希望的那樣,C/C++運行期庫的啟動代碼為應用程序的主線程分配了數據塊,并且對數據塊進行了初始化,這樣,主線程就能安全地調用C/C++運行期函數中的任何函數。當主線它的進入點函數返回時,C/C++運行期庫就會釋放相關的數據塊。此外,啟動代碼設置了Oops—錯誤地調用了也許你想知道,如果調用CreateThread,而不是調用C/C++運行期庫的_beginthreadextiddata結構的C/C++運行期庫函數時,將會發生下面的一些情況(C/C+運行期庫函數都是線程安全函數,不需要該結構。首C/C++運行期庫函數試圖(通過調用TlsGetValue)獲取線程的數據塊的地址。如果返回NULL作為tiddata塊的地址,調用線程就不擁有與該地址相關的tiddata塊。這時,C/C+運行期庫函數就在現場為調用線程分配一個tiddata塊,并對它進行初始化。然后該tiddata塊(通過TlsSetValue)與線程相關聯。此時,只要線程在運行,該tiddata將與線程待在一起。這時,C/C++tiddat塊,而且將來被調用的所有C/C++運行期函數也能使用tiddata塊。當然,這看來有些奇怪,因為線程運行時幾乎沒有任何。不過,實際上還是存在一些問題。首先,如果線程使用C/C++運行期庫的signal函數,那么整個進程就會終止運行,因為結構化異常處理幀尚未準備好。第二,如果不是調用_endthreadex來終止線程的運行,那么數據塊就不會被撤消,內存泄漏就會出現(那么誰還為使用CreateThread函數創建的線程來調用注意如果程序模塊到多線程DLL
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 勤洗手可預防的疾病類型
- 產科出血性疾病診療規范與臨床管理
- Moxifloxacin-d5-BAY-12-8039-d-sub-5-sub-free-base-生命科學試劑-MCE
- 超神數學-高考數學總復習基礎篇(一輪)(練習冊)專題09指數和對數(含答案或解析)
- 家譜:歷史觀的啟蒙班
- 成人教育線上學習模式創新:2025年家庭教育與親子互動研究報告
- 新能源汽車廢舊電池梯次利用項目產業鏈上下游企業競爭力分析報告
- 食品與飲料行業:2025年食品行業食品安全教育與培訓市場潛力與機遇
- 綠色建筑認證體系在綠色建筑標準規范中的應用與發展報告
- 智能健身器材運動監測技術在健身房智能管理中的應用報告
- 教師英語口語訓練課件(完整版)
- 風生水起博主的投資周記
- XXXXX智能化弱電施工組織計劃
- TK校驗儀的使用
- 北京市中小學教師崗位考核登記表(表樣)
- 血栓性淺靜脈炎
- 棄渣場施工及方案
- SolidWorks入門教程(很全面)PPT課件
- 工作聯系函-087,088關于鋁合金窗門安裝滯后影響工期等相關事宜
- 叉車租賃合同
- 投資公司股權投資管理制度
評論
0/150
提交評論