「雙緩衝區」是乙個應用很廣的手法。該手法用得最多的地方想必是螢幕繪製相關的領域(主要是為了減少螢幕閃爍)。另外,在裝置驅動和工控方面,雙緩衝也經常被使用。不過今天要聊的,並不是針對上述的某個具體領域,而是側重於併發方面的同步/互斥開銷。
★為啥要雙緩衝區
記得前幾天在介紹佇列緩衝區 時,提及了普通佇列緩衝區的兩個效能問題:「記憶體分配的開銷」和「同步/互斥的開銷」(健忘的同學,先回去看看那個帖子 複習一下)。「記憶體分配的開銷」已經在介紹環形緩衝區 的時候解決了,而今天要介紹的雙緩衝區,就是衝著同步/互斥的開銷來的。
為了防止有人給咱扣上「過度設計」的大帽子,又得來乙個事先宣告:只有當同步或互斥的開銷非常明顯的時候,你才應該考慮雙緩衝區的使用。否則的話,大夥兒還是老老實實用最基本、最簡單的佇列緩衝區吧。
★雙緩衝區的原理
前面說了一通廢話,現在開始切入正題,說說具體實現。
所謂「雙緩衝區」,故名思義就是要有倆緩衝區(簡稱a和b)。這倆緩衝區,總是乙個用於生產者,另乙個用於消費者。當倆緩衝區都操作完,再進行一次切換(先前被生產者寫入的轉為消費者讀出,先前消費者讀取的轉為生產者寫入)。由於生產者和消費者不會同時操作同乙個緩衝區(不發生衝突),所以就不需要在讀寫每乙個資料單元 的時候都進行同步/互斥操作。順便提一下,這又一次展現了空間換時間的優化思路。
但是光有倆緩衝區還不夠。為了做到「不衝突」,還得再搞兩個互斥鎖(簡稱la和lb),分別對應倆緩衝區。生產者或消費者如果要操作某個緩衝區,必須先擁有對應的互斥鎖。補充一句:要達到「不衝突」的效果,其實可以有多種搞法,今天只是挑乙個簡單的來聊。
★雙緩衝區的幾種狀態
為了加深某些同學的理解,再描述一下雙緩衝區的幾種狀態。
◇倆緩衝區都在使用的狀態(併發讀寫)
大多數情況下,生產者和消費者都處於併發讀寫狀態。不妨設生產者寫入a,消費者讀取b。在這種狀態下,生產者擁有鎖la;同樣的,消費者擁有鎖lb。由於倆緩衝區都是處於獨佔狀態,因此每次讀寫緩衝區中的元素(資料單元 )都不需要再進行加鎖、解鎖操作。這是節約開銷的主要**。
◇單個緩衝區空閒的狀態
由於兩個併發實體的速度會有差異,必然會出現乙個緩衝區已經操作完,而另乙個尚未操作完。不妨假設生產者快於消費者。
在這種情況下,當生產者把a寫滿的時候,生產者要先釋放la(表示它已經不再操作a),然後嘗試獲取lb。由於b還沒有被讀空,lb還被消費者持有,所以生產者進入發呆(suspend)狀態。
◇緩衝區的切換
接著上面的話題。
過了若干時間,消費者終於把b讀完。這時候,消費者也要先釋放lb,然後嘗試獲取la。由於la剛才已經被生產者釋放,所以消費者能立即擁有la並開始讀取a的資料。而由於lb被消費者釋放,所以剛才發呆的生產者會緩過神來(resume)並擁有lb,然後生產者繼續往b寫入資料。
經過上述幾個步驟,倆緩衝區完成了對調,變為:生產者寫入b,消費者讀取a。
★可能的併發問題
本來單個緩衝區的生產者/消費者問題就已經是教科書的經典問題了,現在搞出倆緩衝區,所以就更加耗費腦細胞了。一不小心,就會搞出些併發的bug,而且併發的bug還很難除錯和測試(這也就是為啥不要輕易使用該玩意兒的原因)。
◇死鎖的問題
假如把前面介紹的操作步驟調換一下順序:生產者或消費者在操作完當前的緩衝區之後,先去獲取另乙個緩衝區的鎖,再來釋放當前緩衝區的鎖。那會咋樣捏?
一旦兩個併發實體同時處理完各自緩衝區,然後同時去獲取對方擁有的鎖,那就會出現典型的死鎖(死鎖的詳細解釋參見「這裡 」)場景。它倆從此陷入萬劫不復的境地。
★應用場景
架構設計 生產者 消費者模式 4 雙緩衝區
雙緩衝區 是乙個應用很廣的手法。該手法用得最多的地方想必是螢幕繪製相關的領域 主要是為了減少螢幕閃爍 另外,在裝置驅動和工控方面,雙緩衝也經常被使用。不過今天要聊的,並不是針對上述的某個具體領域,而是側重於併發方面的同步 互斥開銷。為啥要雙緩衝區 記得前幾天在介紹佇列緩衝區時,提及了普通佇列緩衝區的...
架構設計 生產者 消費者模式 4 雙緩衝區
雙緩衝區 是乙個應用很廣的手法。該手法用得最多的地方想必是螢幕繪製相關的領域 主要是為了減少螢幕閃爍 另外,在裝置驅動和工控方面,雙緩衝也經常被使用。不過今天要聊的,並不是針對上述的某個具體領域,而是側重於併發方面的同步 互斥開銷。為啥要雙緩衝區 記得前幾天在介紹佇列緩衝區時,提及了普通佇列緩衝區的...
生產者消費者 生產者與消費者模式
一 什麼是生產者與消費者模式 其實生產者與消費者模式就是乙個多執行緒併發協作的模式,在這個模式中呢,一部分執行緒被用於去生產資料,另一部分執行緒去處理資料,於是便有了形象的生產者與消費者了。而為了更好的優化生產者與消費者的關係,便設立乙個緩衝區,也就相當於乙個資料倉儲,當生產者生產資料時鎖住倉庫,不...