無鎖環形隊列的一種高效實現


 

1.環形隊列是什么 

隊列是一種常用的數據結構,這種結構保證了數據是按照“先進先出”的原則進行操作的,即最先進去的元素也是最先出來的元素.環形隊列是一種特殊的隊列結構,保證了元素也是先進先出的,但與一般隊列的區別是,他們是環形的,即隊列頭部的上個元素是隊列尾部,通常是容納元素數固定的一個閉環。

C代碼實現見:https://github.com/dodng/fast_ring_queue

 

2.環形隊列的優點

 

  1.保證元素是先進先出的

是由隊列的性質保證的,在環形隊列中通過對隊列的順序訪問保證。

 

  2.元素空間可以重復利用

 

因為一般的環形隊列都是一個元素數固定的一個閉環,可以在環形隊列初始化的時候分配好確定的內存空間,當進隊或出隊時只需要返回指定元素內存空間的地址即可,這些內存空間可以重復利用,避免頻繁內存分配和釋放的開銷。

 

  3.為多線程數據通信提供了一種高效的機制。

在最典型的生產者消費者模型中,如果引入環形隊列,那么生成者只需要生成“東西”然后放到環形隊列中即可,而消費者只需要從環形隊列里取“東西”並且消費即可,沒有任何鎖或者等待,巧妙的高效實現了多線程數據通信。

 

3.環形隊列的工作場景

一般應用於需要高效且頻繁進行多線程通信傳遞數據的場景,例如:linux捕包、發包等等,(linux系統中對PACKET_RX_RING和PACKET_TX_RING的支持實質就是內核實現的一種環形隊列)

 

實際環形隊列在工作時有3種情況:

  3.1 入隊速度=出隊速度

這是環形隊列的常態,即入隊速度和出隊速度大致一樣,即使某個突然時刻入隊速度陡然變高或者出隊速度陡然變低,都能通過隊列這個緩沖區把這些數據先存起來,等到能處理的時候再處理。

  3.2 入隊速度>出隊速度

在這種情況下,隊列“寫入”的速度>“讀取”的速度,想象當這種狀態持續一段時間之后,隊列中大多數全是寫入但沒讀取的元素,當又一個新的元素產生時,可以把這個新元素drop掉或者放在另一個緩沖區保存起來,這種情況的出現不是個好事情,說明你需要對出隊處理元素的算法或邏輯優化處理速度了。

 

 

  3.3 入隊速度<出隊速度

           在這種情況下,隊列“讀取”速度>“寫入”速度,這種情況說明程序出隊處理元素的速度很快,這是比較好的情況,唯一不足的是讀取隊列的時候可能經常會輪詢隊列是否有新的元素,造成cpu占用過高。

 

 

4.無鎖環形隊列的實現

  4.1環形隊列的存儲結構

鏈表和線性表都是可以的,但幾乎都用線性表實現,比鏈表快很多,原因也是顯而易見的,因為訪問鏈表需要挨個遍歷。

  4.2讀寫index

有2個index很重要,一個是寫入index,標示了當前可以寫入元素的index,入隊時使用。一個是讀取index,標示了當前可以讀取元素的index,出隊時使用。

      4.3元素狀態切換

有種很巧妙的方法,就是在隊列中每個元素的頭部加一個元素標示字段,標示這個元素是可讀還是可寫,而這個的關鍵就在於何時設置元素的可讀可寫狀態,參照linux內核實現原理,當這個元素讀取完之后,要設置可寫狀態,當這個元素寫入完成之后,要設置可讀狀態。

 

5.實際測試效果

         在CentOS 5.5 (cpu每核主頻1200MHz)設備上簡單測試了一下。環形隊列大小為10000,元素數據大小為4字節,每秒可以寫入並讀取的元素是250萬左右,即250pps.

         C代碼實現見:https://github.com/dodng/fast_ring_queue


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM