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.