第三講一個(gè)線程庫(kù)的實(shí)現(xiàn)_第1頁(yè)
第三講一個(gè)線程庫(kù)的實(shí)現(xiàn)_第2頁(yè)
第三講一個(gè)線程庫(kù)的實(shí)現(xiàn)_第3頁(yè)
第三講一個(gè)線程庫(kù)的實(shí)現(xiàn)_第4頁(yè)
第三講一個(gè)線程庫(kù)的實(shí)現(xiàn)_第5頁(yè)
已閱讀5頁(yè),還剩117頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、李 林 電子科技大學(xué) 計(jì)算機(jī)學(xué)院第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) 本講將討論在Linux平臺(tái)下,如何實(shí)現(xiàn)一個(gè)線程庫(kù),以讓同學(xué)們體會(huì)利用現(xiàn)代程序設(shè)計(jì)思想,進(jìn)行程序開(kāi)發(fā)的方法 幫助同學(xué)們建立從高級(jí)語(yǔ)言到軟件架構(gòu)的映射第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用Linux操作系統(tǒng)的安裝虛擬機(jī)中

2、安裝Linux系統(tǒng)遠(yuǎn)程登錄到Linux服務(wù)器直接安裝Linux系統(tǒng)虛擬機(jī)中安裝Linux在Windows操作系統(tǒng)中,安裝虛擬機(jī)軟件VMware在VMware中安裝Linux操作系統(tǒng)無(wú)需磁盤(pán)分區(qū),可邊使用Windows,邊使用Linux虛擬機(jī)中安裝Linux虛擬機(jī)中安裝Linux遠(yuǎn)程登錄到Linux服務(wù)器使用telnet、SecureCRT等登錄到遠(yuǎn)程Linux服務(wù)器無(wú)需進(jìn)行磁盤(pán)分區(qū),對(duì)原有系統(tǒng)無(wú)影響必須要有Linux服務(wù)器不能獨(dú)占使用,可能存在干擾遠(yuǎn)程登錄到Linux服務(wù)器直接安裝Linux推薦直接磁盤(pán)分區(qū)安裝Linux更快熟悉Linux系統(tǒng)的操作,為什么?對(duì)計(jì)算機(jī)硬件要求低(虛擬機(jī)內(nèi)存要求高

3、)也不要求遠(yuǎn)程服務(wù)器環(huán)境(遠(yuǎn)程登錄)可方便使用集成開(kāi)發(fā)環(huán)境(類(lèi)似于VC開(kāi)發(fā)環(huán)境)推薦安裝Ubuntu強(qiáng)大的軟件包管理工具APT,負(fù)責(zé)下載安裝軟件,并維護(hù)軟件包之間的依賴(lài)關(guān)系Ubuntu桌面版、服務(wù)器版應(yīng)用比較廣泛Ubuntu功能強(qiáng)大,使用方便(邊學(xué)習(xí)邊娛樂(lè))安裝方法Windows與Linux共存選擇一個(gè)Windows非主分區(qū),備份文件,刪除該分區(qū)在該分區(qū)中安裝Linux安裝過(guò)程中,除了創(chuàng)建ext2或ext3文件系統(tǒng)分區(qū)外,還要?jiǎng)?chuàng)建swap交換分區(qū)加入學(xué)校軟件源APT從預(yù)選設(shè)置好的軟件源下載軟件包學(xué)校提供了高速的軟件下載源在/etc/apt/sources.list開(kāi)頭處加入學(xué)校提供的軟件源ht

4、tp:/14g+的使用 Ubuntu默認(rèn)沒(méi)有安裝編譯環(huán)境#apt-get install build-essential g+的基本用法#g+ test.cpp#g+ -o test test.cpp#g+ -c test.cpp 生成目標(biāo)文件test.o#g+ -o test test.cpp g#g+ -O -o test test.cpp 優(yōu)化15g+的使用 g+的基本用法#g+ -S test.cpp 產(chǎn)生匯編代碼test.s#g+ -E test.cpp my.txt 只激活預(yù)處理,將結(jié)果保存在my.txt中#g+ -I./gtest/include test.cpp 指定頭文件路徑

5、#g+ -L./gtest -lgtest test.cpp 指定庫(kù)的路徑#g+ test.cpp DOK=2 設(shè)宏OK為2#g+ test.cpp DOK 定義宏OK make的使用通常在實(shí)際項(xiàng)目中,源文件都會(huì)比較多如果手工輸入g+命令,將會(huì)非常長(zhǎng),怎么辦?代碼3.1第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用日志的實(shí)現(xiàn) 本章圍繞線程模型的程序庫(kù)展開(kāi) 當(dāng)庫(kù)中函數(shù)或系統(tǒng)API出錯(cuò)時(shí),總希望把出錯(cuò)信息記錄下來(lái)。這一任務(wù)通常是由日志來(lái)實(shí)現(xiàn)的 在本庫(kù)中,假設(shè)日志信

6、息被記錄在文件中日志的實(shí)現(xiàn)出錯(cuò)處理CLLog的實(shí)現(xiàn)日志的實(shí)現(xiàn)出錯(cuò)處理CLLog的實(shí)現(xiàn)線程庫(kù)的出錯(cuò)處理 程序庫(kù)對(duì)外提供若干類(lèi),每個(gè)方法出錯(cuò)時(shí)如何告知調(diào)用者是否出錯(cuò),以及出錯(cuò)碼? 方法很多,為了簡(jiǎn)化起見(jiàn),函數(shù)將返回一個(gè)對(duì)象,該對(duì)象保存了函數(shù)的返回值和出錯(cuò)碼線程庫(kù)的出錯(cuò)處理 在線程庫(kù)中,CLStatus類(lèi)作為絕大多數(shù)方法的返回類(lèi)型,其對(duì)象即保存了函數(shù)的返回值和出錯(cuò)碼。 例:CLStatus Run(void *pContext); CLStatus的實(shí)現(xiàn)版本1(代碼3.4)線程庫(kù)的出錯(cuò)處理 在3.4的實(shí)現(xiàn)中,需要調(diào)用GetErrorCode函數(shù)方可獲得出錯(cuò)碼,有無(wú)更為簡(jiǎn)便的方法 直接讓m_lErro

7、rCode成為public成員? 能否有類(lèi)內(nèi)部可讀可寫(xiě),而外部只能讀的數(shù)據(jù)成員?(代碼3.5)CLStatus的使用者效率問(wèn)題 由于CLStatus作為傳值類(lèi)型返回,存在一些效率優(yōu)化的問(wèn)題 代碼3.6CLStatus的使用者效率問(wèn)題 為了兼顧移植性、提高效率,建議CLStatus的使用方式: 例:CLStatus f()return CLStatus();CLStatus s = f();日志的實(shí)現(xiàn)出錯(cuò)處理CLLog的實(shí)現(xiàn)CLLog的實(shí)現(xiàn) CLLog類(lèi)用于記錄程序庫(kù)的日志信息到某個(gè)文件中,例如API調(diào)用出錯(cuò)、類(lèi)庫(kù)中某個(gè)方法調(diào)用出錯(cuò)等等 為了簡(jiǎn)化起見(jiàn),文件中每條日志僅記錄字符串的文字說(shuō)明,以及出

8、錯(cuò)碼 代碼3.7CLLog的實(shí)現(xiàn) 代碼3.7中,每次記錄一條信息,都需要打開(kāi)、關(guān)閉文件,效率不高 解決方法:將打開(kāi)和關(guān)閉文件的操作,放在CLLog的構(gòu)造和析構(gòu)函數(shù)中 代碼3.8CLLog的實(shí)現(xiàn) 效率最高的情況是:在整個(gè)程序的運(yùn)行過(guò)程中,日志文件只被打開(kāi)、關(guān)閉一次。 這樣一來(lái),只能實(shí)例化CLLog一次,并將這次實(shí)例化的對(duì)象在整個(gè)程序中傳遞 如何改進(jìn)?如何保證CLLog只能實(shí)例化一次如何保證程序任何地方,都可以方便地獲取CLLog對(duì)象代碼3.9CLLog的實(shí)現(xiàn) 3.9的示例中,每次調(diào)用GetInstance,都需要調(diào)用者檢查指針是否有效,能否簡(jiǎn)化 示例3.10 CLLog還存在的問(wèn)題?CLLog的

9、實(shí)現(xiàn) CLLog還存在的問(wèn)題多線程同時(shí)記錄日志如何? 目前,CLLog不是線程安全的;后續(xù)再改進(jìn)CLLog的實(shí)現(xiàn) 作業(yè)7 CLLog類(lèi)的實(shí)現(xiàn)不是很高效每次調(diào)用WriteLogMsg時(shí),即調(diào)用了fwrite進(jìn)行I/O操作改進(jìn)的辦法是:CLLog可以提供一個(gè)緩存,如頁(yè)面大小4k用戶(hù)每次調(diào)用WriteLogMsg等接口時(shí),先將日志信息寫(xiě)入緩存待緩存寫(xiě)滿、程序退出、或者用戶(hù)明確指示時(shí),才將緩存內(nèi)容寫(xiě)入日志文件請(qǐng)實(shí)現(xiàn)帶緩存的CLLog類(lèi)日志的實(shí)現(xiàn)出錯(cuò)處理CLLog的實(shí)現(xiàn)第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Wi

10、ndows消息在Linux的重現(xiàn) 線程庫(kù)的使用34線程的創(chuàng)建與終止 pthread_create函數(shù) pthread_join函數(shù) pthread_detach函數(shù)35線程的創(chuàng)建與終止 pthread_create函數(shù) pthread_join函數(shù) pthread_detach函數(shù)36線程的創(chuàng)建 pthread_create函數(shù)用于創(chuàng)建一個(gè)線程 函數(shù)原型#includeint pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void *), void *

11、restrict arg); 參數(shù)與返回值tidp:當(dāng)pthread_create成功返回時(shí),該函數(shù)將線程ID存儲(chǔ)在tidp指向的內(nèi)存區(qū)域中37pthread_create函數(shù) 參數(shù)與返回值attr:用于定制各種不同的線程屬性。通??稍O(shè)為NULL,采用默認(rèn)線程屬性start_rtn:線程的入口函數(shù),即新創(chuàng)建的線程從該函數(shù)開(kāi)始執(zhí)行。該函數(shù)只有一個(gè)參數(shù),即arg,返回一個(gè)指針arg:作為start_rtn的第一個(gè)參數(shù)成功返回0,出錯(cuò)時(shí)返回各種錯(cuò)誤碼38線程的創(chuàng)建 示例3.11編譯:#g+ test.cpp lpthread程序3.11沒(méi)有任何輸出。其原因在于主線程先于新創(chuàng)建的線程退出什么是主線程?

12、 示例3.12如何能夠等待線程的結(jié)束?pthread_join39線程的創(chuàng)建與終止 pthread_create函數(shù) pthread_join函數(shù) pthread_detach函數(shù)40pthread_join函數(shù) 該函數(shù)用于等待某個(gè)線程終止 函數(shù)原型#includeint pthread_join(pthread_t thread, void *rval_ptr); 調(diào)用該函數(shù)的線程將一直阻塞,直到指定的線程退出,如從線程的啟動(dòng)例程中返回41pthread_join函數(shù)int pthread_join(pthread_t thread, void *rval_ptr); 返回值與參數(shù)成功返回0

13、,否則返回錯(cuò)誤編號(hào)thread:需要等待的線程IDrval_ptr:若不關(guān)心線程返回值,可將該參數(shù)設(shè)置為NULL42線程的創(chuàng)建與終止 pthread_create函數(shù) pthread_join函數(shù) pthread_detach函數(shù)43pthread_detach函數(shù) 在默認(rèn)情況下,線程的終止?fàn)顟B(tài)會(huì)保存到對(duì)該線程調(diào)用pthread_join 若線程已經(jīng)處于分離狀態(tài),線程的底層存儲(chǔ)資源可以在線程終止時(shí)立即被收回 當(dāng)線程被分離時(shí),并不能用pthread_join函數(shù)等待它的終止?fàn)顟B(tài),此時(shí)pthread_join返回EINVAL pthread_detach函數(shù)可以使線程進(jìn)入分離狀態(tài)44pthread

14、_detach函數(shù) 函數(shù)原型#includeint pthread_detach(pthread_t tid); 參數(shù)與返回值tid:進(jìn)入分離狀態(tài)的線程的ID成功返回0,出錯(cuò)返回錯(cuò)誤編號(hào) 示例3.13 若pthread_join比pthread_detach先調(diào)用,也能獲取到退出信息45線程的創(chuàng)建與終止 pthread_create函數(shù) pthread_join函數(shù) pthread_detach函數(shù)第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用線程創(chuàng)建的封裝

15、基于對(duì)象的封裝 面向?qū)ο蟮姆庋b 基于接口的封裝48基本對(duì)象的封裝 每次調(diào)用pthread_create很繁瑣,能否簡(jiǎn)化線程的創(chuàng)建工作 示例3.14為什么使用static函數(shù)?為什么需要傳遞this指針?StartFunctionOfThread是private的,為什么能夠被調(diào)用?能否封裝變化點(diǎn)?面向?qū)ο蟮姆庋b 示例3.15為什么析構(gòu)是虛的?能否封裝變化點(diǎn)?耦合度如何?如何理解繼承關(guān)系耦合度高?耦合于接口、耦合于實(shí)現(xiàn)在3.15中,只能通過(guò)創(chuàng)建線程的方式執(zhí)行業(yè)務(wù)邏輯若現(xiàn)在需要通過(guò)創(chuàng)建進(jìn)程的方式執(zhí)行業(yè)務(wù)邏輯,該怎么辦基于接口的封裝 不同的線程,通常處理不同的事情,即業(yè)務(wù)功能 如何對(duì)此變化點(diǎn)(執(zhí)行不

16、同業(yè)務(wù)功能)進(jìn)行封裝 示例3.17 實(shí)現(xiàn)了線程具體業(yè)務(wù)功能的裝配基于接口的封裝 目前是以創(chuàng)建線程的方式執(zhí)行業(yè)務(wù)功能,如果需要?jiǎng)?chuàng)建進(jìn)程的方式執(zhí)行業(yè)務(wù)功能,又當(dāng)如何? 如何對(duì)此變化點(diǎn)(創(chuàng)建不同的執(zhí)行體)進(jìn)行封裝? 示例3.18 實(shí)現(xiàn)了執(zhí)行體的裝配、業(yè)務(wù)功能的裝配 本課程的執(zhí)行體創(chuàng)建模型,以3.18為基礎(chǔ),但后續(xù)還有更多的改造52基于接口的封裝 基于接口的編程模式,更有利于裝配在不改變處理類(lèi)源代碼的情況下,可以自由組合繼承是一種緊耦合的關(guān)系耦合于實(shí)現(xiàn)耦合于接口而基于接口的編程模式,僅耦合于接口第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的

17、封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用54線程的同步 線程同步的概念 互斥量 條件變量55線程同步的概念 為什么需要同步對(duì)同一個(gè)存儲(chǔ)單元,至少存在兩個(gè)線程,其一讀該單元,另一寫(xiě)該單元,則需要同步,避免不一致性在處理器架構(gòu)中,對(duì)內(nèi)存單元的修改,可能需要多個(gè)總線周期,因此讀操作和寫(xiě)操作有可能交織在一起56線程同步的概念 假設(shè)讀操作需要一個(gè)總線周期 寫(xiě)操作需要兩個(gè)總線周期 線程B和線程A沖突57解決上述問(wèn)題的方法 使用鎖,以保證共享存儲(chǔ)一次只能被一個(gè)線程訪問(wèn) 說(shuō)明獲取、釋放鎖的過(guò)程58線程同步的概念 通常,對(duì)一個(gè)存儲(chǔ)單元的訪問(wèn),要經(jīng)歷三個(gè)步驟將內(nèi)存單元中的數(shù)據(jù),讀

18、入寄存器對(duì)寄存器中的值進(jìn)行運(yùn)算將寄存器中的值,寫(xiě)回內(nèi)存單元 無(wú)鎖時(shí)的情況59線程同步 線程同步的概念 互斥量 條件變量60互斥量 可以通過(guò)使用pthread的互斥接口保護(hù)數(shù)據(jù),確保同一時(shí)間里只有一個(gè)線程訪問(wèn)數(shù)據(jù) 互斥量mutex,本質(zhì)上就是一把鎖在訪問(wèn)共享資源前,對(duì)互斥量進(jìn)行加鎖在訪問(wèn)完成后釋放互斥量上的鎖對(duì)互斥量進(jìn)行加鎖后,任何其他試圖再次對(duì)互斥量加鎖的線程將會(huì)被阻塞,直到鎖被釋放61互斥量的初始化 互斥量在使用前,必須要對(duì)互斥量進(jìn)行初始化 函數(shù)原型#includeint pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mute

19、xattr_t *attr); 參數(shù)與返回值mutex:即互斥量,類(lèi)型是pthread_mutex_t注意:mutex必須指向有效的內(nèi)存區(qū)域62互斥量的初始化int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr); 參數(shù)與返回值attr:設(shè)置互斥量的屬性,通常可采用默認(rèn)屬性,即可將attr設(shè)為NULL。成功返回0,出錯(cuò)返回錯(cuò)誤碼63互斥量的銷(xiāo)毀 互斥量在使用完畢后,必須要對(duì)互斥量進(jìn)行銷(xiāo)毀,以釋放資源 函數(shù)原型#includeint pthread_mutex_destroy( pthread_

20、mutex_t *mutex); 參數(shù)與返回值mutex:即互斥量成功返回0,出錯(cuò)返回錯(cuò)誤碼64互斥量的加鎖和解鎖操作 在對(duì)共享資源訪問(wèn)之前和訪問(wèn)之后,需要對(duì)互斥量進(jìn)行加鎖和解鎖操作 函數(shù)原型#includeint pthread_mutex_lock(pthread_mutex_t *mutex);Int pthread_mutex_unlock(pthread_mutex_t *mutex); 回憶鎖的語(yǔ)義65互斥量的操作順序 定義一個(gè)互斥量pthread_mutex_t 調(diào)用pthread_mutex_init初始化互斥量 調(diào)用pthread_mutex_lock對(duì)互斥量進(jìn)行加鎖操作 調(diào)

21、用pthread_mutex_unlock對(duì)互斥量解鎖 調(diào)用pthread_mutex_destroy銷(xiāo)毀互斥量 示例3.21(在示例3.18基礎(chǔ)之上)CLLog的改進(jìn) 3.10示例中,CLLog的實(shí)現(xiàn)不是線程安全的寫(xiě)文件時(shí)的不安全創(chuàng)建CLLog唯一對(duì)象時(shí)的不安全 示例3.22(在3.10基礎(chǔ)之上)解決了寫(xiě)文件時(shí)的不安全問(wèn)題 示例3.23(在3.22基礎(chǔ)之上)保證了多線程環(huán)境下只會(huì)創(chuàng)建一個(gè)對(duì)象CLLog的改進(jìn) 示例3.23存在的問(wèn)題CLLog類(lèi)型的對(duì)象只會(huì)被創(chuàng)建一次,其后都將是讀操作但每次讀都需要加鎖、解鎖,效率不高能否讓后續(xù)的讀操作,不再加鎖 示例3.24雙檢測(cè)機(jī)制m_pLog為什么是vol

22、atile?volatile變量 volatile變量:一般在多線程中使用的比較多例如有一個(gè)int x,有兩個(gè)線程都要對(duì)其讀寫(xiě)有些編譯器或CPU會(huì)將x保存在寄存器中,讀的時(shí)候直接讀取寄存器中的內(nèi)容,而不是真實(shí)的x在內(nèi)存中的內(nèi)容線程1,對(duì)x進(jìn)行加1操作,此時(shí)內(nèi)存中x的值為2線程2想讀x,結(jié)果從寄存器中讀出1給變量加上volatile,指示程序每次讀寫(xiě)變量都必須從內(nèi)存中讀取,不要進(jìn)行緩存(寄存器)互斥量的封裝 每次使用互斥量都需要調(diào)用pthread_mutex_init 和pthread_mutex_destroy函數(shù),能否簡(jiǎn)化 示例3.25 (在示例3.18基礎(chǔ)之上)70互斥量的封裝 獲取鎖之后

23、,一定要釋放鎖 但是有時(shí)候鎖的釋放并不容易被控制 示例3.26 (在示例3.18基礎(chǔ)之上)為什么主線程被阻塞?在mutex.Lock()和mutex_Unlock()之間若存在復(fù)雜的函數(shù)調(diào)用,異常處理又當(dāng)如何?每次訪問(wèn)臨界區(qū),都需要顯示調(diào)用加鎖和解鎖,能否簡(jiǎn)化臨界區(qū)的封裝 示例3.27 (在示例3.18基礎(chǔ)之上) 無(wú)論是異常退出還是中途調(diào)用return退出,都能保證解鎖 互斥量的不同范圍,能提供不同程度的互斥類(lèi)級(jí)別的(static的互斥量)對(duì)象級(jí)別的(普通數(shù)據(jù)成員的互斥量)72線程同步 線程同步的概念 互斥量 條件變量73條件變量 現(xiàn)有一需求,線程A先執(zhí)行某操作后,線程B才能執(zhí)行另一操作,該如

24、何實(shí)現(xiàn)? 條件變量與互斥量一起使用時(shí),允許線程以無(wú)競(jìng)爭(zhēng)的方式等待特定條件的發(fā)生 與互斥量類(lèi)似,條件變量也需要初始化和銷(xiāo)毀74條件變量的初始化和銷(xiāo)毀 函數(shù)原型#includeint pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);int pthread_cond_destroy(pthread_cond_t * cond); 參數(shù)和返回值cond:條件變量attr:條件變量屬性,若為NULL,則使用默認(rèn)屬性成功返回0,出錯(cuò)返回錯(cuò)誤編號(hào)75等待條件的發(fā)生 pthread_cond_wait函數(shù)將使調(diào)用線程進(jìn)入阻塞狀

25、態(tài),直到條件為真 函數(shù)原型#includeint pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); 參數(shù)與返回值cond:條件變量mutex:互斥量成功返回0,否則返回錯(cuò)誤編號(hào) 76等待條件的發(fā)生 為什么pthread_cond_wait需要互斥量在調(diào)用pthread_cond_wait前,需要使互斥量處于鎖住狀態(tài)這樣pthread_cond_wait函數(shù),可以以原子的方式,將調(diào)用線程放到等待條件的線程列表上 pthread_cond_wait函數(shù)的特殊操作在線程阻塞前,調(diào)用pthread_mutex_unlock

26、在線程喚醒后,調(diào)用pthread_mutex_lock77等待條件的發(fā)生 等待線程的操作順序調(diào)用pthread_mutex_lock調(diào)用pthread_cond_wait調(diào)用pthread_mutex_unlock78使等待的條件為真 pthread_cond_signal和pthread_cond_broadcast可以通知等待的線程,條件已經(jīng)滿足。 pthread_cond_signal喚醒某一個(gè)等待該條件的線程 pthread_cond_broadcast喚醒等待該條件的所有線程79等待條件為真 函數(shù)原型#includeint pthread_cond_signal(pthread_co

27、nd_t *cond);int pthread_cond_broadcast(pthread_cond_t *); 參數(shù)與返回值cond:條件變量成功返回0,否則返回錯(cuò)誤編號(hào)80條件變量的封裝 示例3.28(在示例3.18基礎(chǔ)之上)為什么主線程陷入阻塞狀態(tài)而不返回?為什么子線程加入sleep后正常?81條件變量 上例出錯(cuò)的原因子線程調(diào)用pthread_cond_signal后,主線程才調(diào)用pthread_cond_wait進(jìn)入阻塞狀態(tài)這樣,主線程就一直無(wú)法被喚醒 解決方案示例3.29(在示例3.18基礎(chǔ)之上)82條件變量 等待線程調(diào)用pthread_mutex_lockWhile(判斷條件)p

28、thread_cond_wait重復(fù)檢查條件是由于線程可能不是被pthread_cond_signal喚醒,可能是由信號(hào)等喚醒(futex)調(diào)用pthread_mutex_unlock 被等待線程調(diào)用pthread_mutex_lock修改條件調(diào)用pthread_mutex_unlock調(diào)用pthread_mutex_broadcast等83事件的封裝 條件變量的處理比較復(fù)雜,需要有flag變量、固定的函數(shù)調(diào)用序列等等 能否簡(jiǎn)化條件變量的使用 封裝的思路:Windows的事件機(jī)制讓一個(gè)線程等待某一個(gè)事件的發(fā)生 示例3.30(在示例3.18基礎(chǔ)之上)為什么m_Flag是volatile的84線程

29、的同步 線程同步的概念 互斥量 條件變量第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用線程創(chuàng)建的再封裝 一個(gè)CLThread類(lèi)的對(duì)象,應(yīng)該只對(duì)應(yīng)一個(gè)線程 用戶(hù)如果按照如下方式使用,會(huì)有什么后果?CLThread *p = .;p-Run();p-Run();線程創(chuàng)建的再封裝 前一次創(chuàng)建的線程,可能無(wú)法再被控制。例如:m_ThreadID僅為剛創(chuàng)建線程的ID,調(diào)用WaitForDeath,將無(wú)法等待上一個(gè)線程的結(jié)束 如何解決,以防止在一個(gè)CLThread對(duì)象上,

30、多次調(diào)用Run方法? 示例3.31 (在示例3.18基礎(chǔ)之上)線程創(chuàng)建的再封裝 通常情況下,CLThread類(lèi)及CLExecutiveFunctionProvider派生類(lèi)的對(duì)象,應(yīng)從堆中分配。為什么不是棧上? 從堆中分配,就需要顯式的釋放,能否簡(jiǎn)化?能否讓類(lèi)的使用者,只分配對(duì)象,而不用調(diào)用delete? 示例3.32 (在示例3.31基礎(chǔ)之上)線程創(chuàng)建的再封裝 在有些情況下,新線程的創(chuàng)建者,可能不需要等待新線程死亡,即不調(diào)用WaitForDeath 這樣3.32的釋放對(duì)象方式將失效 示例3.33(在示例3.32基礎(chǔ)之上)增加一個(gè)標(biāo)識(shí),讓用戶(hù)指定他是否需要等待新線程死亡線程創(chuàng)建的再封裝 產(chǎn)生異

31、常的原因在于:新線程在主線程調(diào)用WaitForDeath之前就已經(jīng)退出了甚至有可能在主線程剛調(diào)用完pthread_create之后,就退出了 解決辦法:需要保證在Run方法退出之前,新線程不會(huì)死亡若用戶(hù)選擇了不等待新線程死亡,則他就不應(yīng)該再調(diào)用WaitForDeath另外,從語(yǔ)義上講,Run方法返回前,應(yīng)保證新線程確實(shí)已經(jīng)被創(chuàng)建了示例3.34(在示例3.33基礎(chǔ)之上)第三講 一個(gè)線程庫(kù)的實(shí)現(xiàn) Linux的基本編程環(huán)境 日志的實(shí)現(xiàn) 線程的創(chuàng)建與終止 線程創(chuàng)建的封裝 線程同步的封裝 線程創(chuàng)建的再封裝 Windows消息在Linux的重現(xiàn) 線程庫(kù)的使用Windows消息在Linux的重現(xiàn) 在wind

32、ows中,進(jìn)行線程之間的通信十分簡(jiǎn)便。Windows系統(tǒng)為每個(gè)線程都配備了一個(gè)消息隊(duì)列 調(diào)用PostThreadMessage函數(shù),可以向一個(gè)線程發(fā)送消息 調(diào)用GetMessage函數(shù),可以從消息隊(duì)列中獲取一個(gè)消息生產(chǎn)者/消費(fèi)者模型 消息隊(duì)列及機(jī)制,是一種典型的生產(chǎn)者/消費(fèi)者模型應(yīng)用。 發(fā)送消息的線程(生產(chǎn)者):將消息投入到消息隊(duì)列后,即返回 消息的接收線程(消費(fèi)者):一直處于阻塞等待狀態(tài),直到有線程將消息投入到消息隊(duì)列中 我們的目標(biāo):建立一個(gè)自定義的消息隊(duì)列,以實(shí)現(xiàn)同一進(jìn)程內(nèi)線程之間的通信Windows消息在Linux的重現(xiàn) 消息的封裝 自定義消息隊(duì)列的實(shí)現(xiàn) 消息循環(huán)機(jī)制的封裝 創(chuàng)建線程與消

33、息循環(huán)的結(jié)合 簡(jiǎn)單的名字服務(wù) 使用的簡(jiǎn)化Windows消息在Linux的重現(xiàn) 消息的封裝 自定義消息隊(duì)列的實(shí)現(xiàn) 消息循環(huán)機(jī)制的封裝 創(chuàng)建線程與消息循環(huán)的結(jié)合 簡(jiǎn)單的名字服務(wù) 使用的簡(jiǎn)化消息的封裝 Windows的線程消息:BOOL WINAPI PostThreadMessage(DWORD idThread,UINT Msg, WPARAM wParam, LPARAM lParam); Windows的消息包含了線程ID,以及兩個(gè)自定義參數(shù),不符合面向?qū)ο蟮乃枷?需要建立起有關(guān)消息的繼承體系消息的繼承體系 示例3.35 CLMessage是對(duì)消息的抽象,僅包含了消息的ID 線程之間發(fā)送的具

34、體消息,需要從CLMessage派生,并定義屬于該消息自己的參數(shù)Windows消息在Linux的重現(xiàn) 消息的封裝 自定義消息隊(duì)列的實(shí)現(xiàn) 消息循環(huán)機(jī)制的封裝 創(chuàng)建線程與消息循環(huán)的結(jié)合 簡(jiǎn)單的名字服務(wù) 使用的簡(jiǎn)化自定義消息隊(duì)列的實(shí)現(xiàn) 隊(duì)列本身采用STL庫(kù)中的queue類(lèi),該類(lèi)型的隊(duì)列能自動(dòng)增長(zhǎng) 必須以線程安全的方式,將消息放入到隊(duì)列中,然后通知等待的線程消息已到達(dá) 通知的機(jī)制可以采用CLEvent類(lèi),但3.30示例中的CLEvent必須改造 為什么? 示例3.36自定義消息隊(duì)列的實(shí)現(xiàn) CLMessageQueue的實(shí)現(xiàn) 示例3.37 在上例中,采用STL的queue實(shí)現(xiàn)了消息隊(duì)列 也可以采用其他方

35、式實(shí)現(xiàn),例如管道、網(wǎng)絡(luò)通信、自定義的隊(duì)列等等 如何封裝這一變化點(diǎn)?它們之間是否有共通的部分(均為生產(chǎn)者/消費(fèi)者模型應(yīng)用)Windows消息在Linux的重現(xiàn) 消息的封裝 自定義消息隊(duì)列的實(shí)現(xiàn) 消息循環(huán)機(jī)制的封裝 創(chuàng)建線程與消息循環(huán)的結(jié)合 簡(jiǎn)單的名字服務(wù) 使用的簡(jiǎn)化消息循環(huán)機(jī)制的封裝 作為消費(fèi)者,即消息接收者,總有如下偽代碼:While(true)pMsg = GetMessage();DispatchMessage(pMsg); 上述循環(huán),通常被稱(chēng)為消息循環(huán) 每種消息隊(duì)列基本上都需要上述消息循環(huán)框架,如何封裝消息循環(huán)機(jī)制的封裝 自然地想法:建立繼承體系,把消息循環(huán)框架放入到基類(lèi)中 示例3.38

36、消息循環(huán)機(jī)制的封裝 示例3.38存在的問(wèn)題處理具體消息的CLMyMsgProcessor類(lèi),從CLMsgLoopManagerForMsgQueue繼承而來(lái),是一種強(qiáng)耦合關(guān)系。這樣一來(lái),消息的具體處理邏輯,就和采用STL的queue綁定在了一起。即只能處理來(lái)自于queue的消息若消息是從管道,或者其他方式而來(lái),但消息處理邏輯本身是一樣的,該當(dāng)如何?如何裝配、封裝這一變化點(diǎn)?另外,繼承體系通常不要超過(guò)兩層示例3.39消息循環(huán)機(jī)制的封裝 在CLMessageObserver的MessageDispatch函數(shù)中,使用了switch/case,進(jìn)行消息的分派,符合面向?qū)ο髥幔?能否消除掉switch

37、/case、if/else,直接可以把消息發(fā)送到相應(yīng)的類(lèi)或函數(shù)中 示例3.40一個(gè)類(lèi)對(duì)應(yīng)一個(gè)消息處理,雖然符合面向?qū)ο笏枷耄^(guò)于繁瑣Windows消息在Linux的重現(xiàn) 消息的封裝 自定義消息隊(duì)列的實(shí)現(xiàn) 消息循環(huán)機(jī)制的封裝 創(chuàng)建線程與消息循環(huán)的結(jié)合 簡(jiǎn)單的名字服務(wù) 使用的簡(jiǎn)化創(chuàng)建線程與消息循環(huán)的結(jié)合 將創(chuàng)建線程的繼承體系,與消息循環(huán)的繼承體系結(jié)合在一起 示例3.41 在某些情況下,主線程向新線程發(fā)送消息時(shí),新線程的消息隊(duì)列可能還未建立好,怎么辦?Windows消息在Linux的重現(xiàn) 消息的封裝 自定義消息隊(duì)列的實(shí)現(xiàn) 消息循環(huán)機(jī)制的封裝 創(chuàng)建線程與消息循環(huán)的結(jié)合 簡(jiǎn)單的名字服務(wù) 使用的簡(jiǎn)化簡(jiǎn)單

38、的名字服務(wù) 在windows中線程通信,只需要指定對(duì)方的線程ID,然后調(diào)用相應(yīng)API即可操作系統(tǒng)提供了許多輔助,通過(guò)ID能找到對(duì)應(yīng)線程的隊(duì)列 在示例3.41中,線程之間通信,需要通信雙方保留CLMessageQueue的對(duì)象指針,非常麻煩 多個(gè)線程間保留同一個(gè)CLMessageQueue的對(duì)象指針,何時(shí)釋放?簡(jiǎn)單的名字服務(wù) 解決思路:為每個(gè)線程賦予一個(gè)獨(dú)一無(wú)二的名稱(chēng)建立線程名稱(chēng),到與該線程通信的通信對(duì)象的關(guān)聯(lián)表提供一個(gè)單例對(duì)象即名字服務(wù),維護(hù)該表,并提供查詢(xún)功能(給出線程名稱(chēng),返回對(duì)應(yīng)的通信對(duì)象)采用引用計(jì)數(shù)方式管理通信對(duì)象示例3.42CLExecutiveCommunicationCLThreadCommunicationByMsgQueueCLExecutiveNameServerCLMsgLoopManagerForMsgQueue簡(jiǎn)單的名字服務(wù) 3.42中,每次都需要檢查名字服務(wù)對(duì)象指針、通信對(duì)象指針有效性,并保證調(diào)用Release操作,十分麻煩,如何簡(jiǎn)化? 示例3.43Wind

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論