第三講一個線程庫的實現_第1頁
第三講一個線程庫的實現_第2頁
第三講一個線程庫的實現_第3頁
第三講一個線程庫的實現_第4頁
第三講一個線程庫的實現_第5頁
已閱讀5頁,還剩117頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

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

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

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

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

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

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

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

8、錯碼 代碼3.7CLLog的實現 代碼3.7中,每次記錄一條信息,都需要打開、關閉文件,效率不高 解決方法:將打開和關閉文件的操作,放在CLLog的構造和析構函數中 代碼3.8CLLog的實現 效率最高的情況是:在整個程序的運行過程中,日志文件只被打開、關閉一次。 這樣一來,只能實例化CLLog一次,并將這次實例化的對象在整個程序中傳遞 如何改進?如何保證CLLog只能實例化一次如何保證程序任何地方,都可以方便地獲取CLLog對象代碼3.9CLLog的實現 3.9的示例中,每次調用GetInstance,都需要調用者檢查指針是否有效,能否簡化 示例3.10 CLLog還存在的問題?CLLog的

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

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

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

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

13、,否則返回錯誤編號thread:需要等待的線程IDrval_ptr:若不關心線程返回值,可將該參數設置為NULL42線程的創(chuàng)建與終止 pthread_create函數 pthread_join函數 pthread_detach函數43pthread_detach函數 在默認情況下,線程的終止狀態(tài)會保存到對該線程調用pthread_join 若線程已經處于分離狀態(tài),線程的底層存儲資源可以在線程終止時立即被收回 當線程被分離時,并不能用pthread_join函數等待它的終止狀態(tài),此時pthread_join返回EINVAL pthread_detach函數可以使線程進入分離狀態(tài)44pthread

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

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

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

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

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

19、xattr_t *attr); 參數與返回值mutex:即互斥量,類型是pthread_mutex_t注意:mutex必須指向有效的內存區(qū)域62互斥量的初始化int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr); 參數與返回值attr:設置互斥量的屬性,通??刹捎媚J屬性,即可將attr設為NULL。成功返回0,出錯返回錯誤碼63互斥量的銷毀 互斥量在使用完畢后,必須要對互斥量進行銷毀,以釋放資源 函數原型#includeint pthread_mutex_destroy( pthread_

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

37、/case、if/else,直接可以把消息發(fā)送到相應的類或函數中 示例3.40一個類對應一個消息處理,雖然符合面向對象思想,但過于繁瑣Windows消息在Linux的重現 消息的封裝 自定義消息隊列的實現 消息循環(huán)機制的封裝 創(chuàng)建線程與消息循環(huán)的結合 簡單的名字服務 使用的簡化創(chuàng)建線程與消息循環(huán)的結合 將創(chuàng)建線程的繼承體系,與消息循環(huán)的繼承體系結合在一起 示例3.41 在某些情況下,主線程向新線程發(fā)送消息時,新線程的消息隊列可能還未建立好,怎么辦?Windows消息在Linux的重現 消息的封裝 自定義消息隊列的實現 消息循環(huán)機制的封裝 創(chuàng)建線程與消息循環(huán)的結合 簡單的名字服務 使用的簡化簡單

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

溫馨提示

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

評論

0/150

提交評論