天天看點

伺服器設計筆記(3)-----消息隊列

    摘抄的一篇文章,故拿出來記錄下,下篇部落格把解決代碼分享出來。感謝這篇文章的原作者,解決了棘手的問題。

    我們所能想到的最簡單的消息隊列可能就是使用stl的list來實作了,即消息隊列内部維護一個list和一個互斥鎖,putmessage時将message加入到隊列尾,getmessage時從隊列頭取一個message傳回,同時在getmessage和putmessage之前都要求先擷取鎖資源。

實作雖然簡單,但功能是絕對滿足需求的,隻是性能上可能稍稍有些不盡如人意。其最大的問題在頻繁的鎖競争上。

對于如何減少鎖競争次數的優化方案,ghost cheng提出了一種。提供一個隊列容器,裡面有多個隊列,每個隊列都可固定存放一定數量的消息。網絡io線程要給邏輯線程投遞消息時,會從隊列容器中取一個空隊列來使用,直到将該隊列填滿後再放回容器中換另一個空隊列。而邏輯線程取消息時是從隊列容器中取一個有消息的隊列來讀取,處理完後清空隊列再放回到容器中。

這樣便使得隻有在對隊列容器進行操作時才需要加鎖,而io線程和邏輯線程在操作自己目前使用的隊列時都不需要加鎖,是以鎖競争的機會大大減少了。

這裡為每個隊列設了個最大消息數,看來好像是打算隻有當io線程寫滿隊列時才會将其放回到容器中換另一個隊列。那這樣有時也會出現io線程未寫滿一個隊列,而邏輯線程又沒有資料可處理的情況,特别是當資料量很少時可能會很容易出現。ghost cheng在他的描述中沒有講到如何解決這種問題,但我們可以先來看看另一個方案。

這個方案與上一個方案基本類似,隻是不再提供隊列容器,因為在這個方案中隻使用了兩個隊列,arthur在他的一封郵件中描述了這個方案的實作及部分代碼。兩個隊列,一個給邏輯線程讀,一個給io線程用來寫,當邏輯線程讀完隊列後會将自己的隊列與io線程的隊列相調換。是以,這種方案下加鎖的次數會比較多一些,io線程每次寫隊列時都要加鎖,邏輯線程在調換隊列時也需要加鎖,但邏輯線程在讀隊列時是不需要加鎖的。

雖然看起來鎖的調用次數是比前一種方案要多很多,但實際上大部分鎖調用都是不會引起阻塞的,隻有在邏輯線程調換隊列的那一瞬間可能會使得某個線程阻塞一下。另外對于鎖調用過程本身來說,其開銷是完全可以忽略的,我們所不能忍受的僅僅是因為鎖調用而引起的阻塞而已。  

繼續閱讀