Posted on 2011-01-19 14:57
點(diǎn)點(diǎn)滴滴 閱讀(986)
評論(0) 編輯 收藏 引用 所屬分類:
10 服務(wù)器
既然說到了消息隊(duì)列,那我們繼續(xù)來稍微多聊一點(diǎn)吧。
我們所能想到的最簡單的消息隊(duì)列可能就是使用stl的list來實(shí)現(xiàn)了,即消息隊(duì)列內(nèi)部維護(hù)一個(gè)list和一個(gè)互斥鎖,putMessage時(shí)將message加入到隊(duì)列尾,getMessage時(shí)從隊(duì)列頭取一個(gè)message返回,同時(shí)在getMessage和putMessage之前都要求先獲取鎖資源。
實(shí)現(xiàn)雖然簡單,但功能是絕對滿足需求的,只是性能上可能稍稍有些不盡如人意。其最大的問題在頻繁的鎖競爭上。
對于如何減少鎖競爭次數(shù)的優(yōu)化方案,Ghost Cheng提出了一種。提供一個(gè)隊(duì)列容器,里面有多個(gè)隊(duì)列,每個(gè)隊(duì)列都可固定存放一定數(shù)量的消息。網(wǎng)絡(luò)IO線程要給邏輯線程投遞消息時(shí),會(huì)從隊(duì)列容器中取一個(gè)空隊(duì)列來使用,直到將該隊(duì)列填滿后再放回容器中換另一個(gè)空隊(duì)列。而邏輯線程取消息時(shí)是從隊(duì)列容器中取一個(gè)有消息的隊(duì)列來讀取,處理完后清空隊(duì)列再放回到容器中。
這樣便使得只有在對隊(duì)列容器進(jìn)行操作時(shí)才需要加鎖,而IO線程和邏輯線程在操作自己當(dāng)前使用的隊(duì)列時(shí)都不需要加鎖,所以鎖競爭的機(jī)會(huì)大大減少了。
這里為每個(gè)隊(duì)列設(shè)了個(gè)最大消息數(shù),看來好像是打算只有當(dāng)IO線程寫滿隊(duì)列時(shí)才會(huì)將其放回到容器中換另一個(gè)隊(duì)列。那這樣有時(shí)也會(huì)出現(xiàn)IO線程未寫滿一個(gè)隊(duì)列,而邏輯線程又沒有數(shù)據(jù)可處理的情況,特別是當(dāng)數(shù)據(jù)量很少時(shí)可能會(huì)很容易出現(xiàn)。Ghost Cheng在他的描述中沒有講到如何解決這種問題,但我們可以先來看看另一個(gè)方案。
這個(gè)方案與上一個(gè)方案基本類似,只是不再提供隊(duì)列容器,因?yàn)樵谶@個(gè)方案中只使用了兩個(gè)隊(duì)列,arthur在他的一封郵件中描述了這個(gè)方案的實(shí)現(xiàn)及部分代碼。兩個(gè)隊(duì)列,一個(gè)給邏輯線程讀,一個(gè)給IO線程用來寫,當(dāng)邏輯線程讀完隊(duì)列后會(huì)將自己的隊(duì)列與IO線程的隊(duì)列相調(diào)換。所以,這種方案下加鎖的次數(shù)會(huì)比較多一些,IO線程每次寫隊(duì)列時(shí)都要加鎖,邏輯線程在調(diào)換隊(duì)列時(shí)也需要加鎖,但邏輯線程在讀隊(duì)列時(shí)是不需要加鎖的。
雖然看起來鎖的調(diào)用次數(shù)是比前一種方案要多很多,但實(shí)際上大部分鎖調(diào)用都是不會(huì)引起阻塞的,只有在邏輯線程調(diào)換隊(duì)列的那一瞬間可能會(huì)使得某個(gè)線程阻塞一下。另外對于鎖調(diào)用過程本身來說,其開銷是完全可以忽略的,我們所不能忍受的僅僅是因?yàn)殒i調(diào)用而引起的阻塞而已。
兩種方案都是很優(yōu)秀的優(yōu)化方案,但也都是有其適用范圍的。Ghost Cheng的方案因?yàn)樘峁┝硕鄠€(gè)隊(duì)列,可以使得多個(gè)IO線程可以總工程師的,互不干擾的使用自己的隊(duì)列,只是還有一個(gè)遺留問題我們還不了解其解決方法。arthur的方案很好的解決了上一個(gè)方案遺留的問題,但因?yàn)橹挥幸粋€(gè)寫隊(duì)列,所以當(dāng)想要提供多個(gè)IO線程時(shí),線程間互斥地寫入數(shù)據(jù)可能會(huì)增大競爭的機(jī)會(huì),當(dāng)然,如果只有一個(gè)IO線程那將是非常完美的。
理解:
1).提供一個(gè)隊(duì)列容器,里面有多個(gè)隊(duì)列,每個(gè)隊(duì)列都可固定存放一定數(shù)量的消息。網(wǎng)絡(luò)IO線程要給邏輯線程投遞消息時(shí),會(huì)從隊(duì)列容器中取一個(gè)空隊(duì)列來使用,直到將該隊(duì)列填滿后再放回容器中換另一個(gè)空隊(duì)列。而邏輯線程取消息時(shí)是從隊(duì)列容器中取一個(gè)有消息的隊(duì)列來讀取,處理完后清空隊(duì)列再放回到容器中。
2).交換隊(duì)列,一個(gè)給邏輯線程讀,一個(gè)給IO線程用來寫,當(dāng)邏輯線程讀完隊(duì)列后會(huì)將自己的隊(duì)列與IO線程的隊(duì)列相調(diào)換