1. 程式人生 > >生產者、消費者模型

生產者、消費者模型

一對多 程序 圖片 water 三生 情人節 itl 未處理 代碼

轉載地址:http://blog.csdn.net/snow_5288/article/details/72794306

一、概念引入

日常生活中,每當我們缺少某些生活用品時,我們都會去超市進行購買,那麽,你有沒有想過,你是以什麽身份去的超市呢?相信大部分人都會說自己是消費者,確實如此,那麽既然我們是消費者,又是誰替我們生產各種各樣的商品呢?當然是超市的各大供貨商,自然而然地也就成了我們的生產者。如此一來,生產者有了,消費者也有了,那麽將二者聯系起來的超市又該作何理解呢?誠然,它本身是作為一座交易場所而誕生。

將上述場景例比到我們實際的軟件開發過程中,經常會見到這樣一幕:代碼的某個模塊負責生產數據(供貨商),而生產出來的數據卻不得不交給另一模塊(消費者)來對其進行處理,在這之間我們必須要有一個類似上述超市的東西來存儲數據(超市),這就抽象除了我們的生產者/消費者模型。
其中,產生數據的模塊,就形象地稱為生產者

;而處理數據的模塊,就稱為消費者;生產者和消費者之間的中介就叫做緩沖區

三者之間的結構圖:

    技術分享

為了方便理解,再列舉一個寄信的例子:
1、你把信寫好——相當於生產者制造數據
2、你把信放入郵筒——相當於生產者把數據放入緩沖區
3、郵遞員把信從郵筒取出——相當於消費者把數據取出緩沖區
4、郵遞員把信拿去郵局做相應的處理——相當於消費者處理數據

二、為什麽要使用生產者消費者模型

歸根結底來說,生產者消費者模式就是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之後不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就相當於一個緩沖區,平衡了生產者和消費者的處理能力。這個阻塞隊列就是用來給生產者和消費者解耦的。

在線程世界裏,生產者就是生產數據的線程,消費者就是消費數據的線程。在多線程開發當中,如果生產者處理速度很快,而消費者處理速度很慢,那麽生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大於生產者,那麽消費者就必須等待生產者。為了解決這種生產消費能力不均衡的問題,所以便有了生產者和消費者模式。

三、生產者/消費者模型的優點

1、解耦,即降低生產者和消費者之間的依賴關系。
例如上述寫信的例子,如果不使用郵筒(也就是緩區),你必須得把信直接交給郵遞員。有同學會說,直接給郵遞員不是挺簡單的嘛?其實不簡單,你必須得認識誰是郵遞員,才能把信給他(光憑身上穿的制服,萬一有人假冒,就慘了 )。這就產生和你和郵遞員之間的依賴(相當於生產者和消費者的強耦合

)。萬一哪天郵遞員換人了,你還要重新認識一下(相當於消費者變化導致修改生產者代碼)。而郵筒相對來說比較固定,你依賴它的成本就比較低(相當於和緩沖區之間的弱耦合)。

2、支持並發,即生產者和消費者可以是兩個獨立的並發主體,互不幹擾的運行。
從寄信的例子來看。如果沒有郵筒,你得拿著信傻站在路口等郵遞員過來收(相當於生產者阻塞);又或者郵遞員得挨家挨戶問,誰要寄信(相當於消費者輪詢)。不管是哪種方法,效率都比較低。

3、支持忙閑不均,如果制造數據的速度時快時慢,緩沖區可以對其進行適當緩沖。當數據制造快的時候,消費者來不及處理,未處理的數據可以暫時存在緩沖區中。等生產者的制造速度慢下來,消費者再慢慢處理掉。
為了充分復用,我們再拿寄信的例子來說事。假設郵遞員一次只能帶走1000封信。萬一某次碰上情人節(也可能是聖誕節)送賀卡,需要寄出去的信超過1000封,這時候郵筒這個緩沖區就派上用場了。郵遞員把來不及帶走的信暫存在郵筒中,等下次過來時再拿走。

四、生產者/消費者模型的記憶原則

為了方便記憶,我對其進行了如下總結:

三二一原則:三種關系、兩個角色、一個場所

三種關系
1生產者與生產者(互斥)
2生產者與消費者(同步與互斥)
3消費者與消費者(互斥)

兩個角色
1生產者
2消費者
一個場所
1緩沖區

五、生產者/消費者模型的學習旅程

1、確定數據單元
★啥是數據單元
向緩沖區拿放數據的一個基本數據單元。簡單地說,每次生產者放到緩沖區的,就是一個數據單元;每次消費者從緩沖區取出的,也是一個數據單元。
★數據單元的特性
◇關聯到業務對象:數據單元必須關聯到某種業務對象
◇完整性:保證每一個數據單元的完整
◇獨立性:各個數據單元之間沒有互相依賴
◇顆粒度:業務對象和數據單元之間的對應比例

2、學習隊列緩沖區
★線程方式
◇內存分配的性能:內存分配的開銷問題—->環形緩沖區
◇同步和互斥的性能:例如信號量、互斥量等的開銷—->雙緩沖區
◇適用於隊列的場合:適用於數據流量不是很大的場合
★進程方式
◇匿名管道:生產者進程在管道的寫端放入數據;消費者
進程在管道的讀端取出數據。

好處:
1》跨平臺發方便。
2》跨語言方便。
3》有利於降低開發、調試成本。
不足:
1》生產者進程和消費者進程必須得在同一臺主機上,無法跨機器通訊。
2》只適用於一對一通信。
3》在某些情況下,程序不便於對管道進行操縱(比如調整管道緩沖區尺寸)。
4》只能單向通信。
◇SOCKET(TCP方式):類似IPC方式,同樣保證了
數據的順序到達;同樣有緩沖的機制。
優點:
1》可以跨機器(便於實現分布式)。
2》便於將來擴展成為多對一或者一對多。
3》可以設置阻塞和非阻塞方法,用起來比較靈活。
4》支持雙向通訊,有利於消費者反饋信息。
不足:
編程復雜。
雖然TCP在很多方面比UDP可靠,但鑒於跨機器通訊先天的不可預料性,可以在生產者進程和消費者進程內部各自再引入基於線程的”生產者/消費者模式”。


技術分享

3、學習環形緩沖區:
★環形緩沖區與隊列緩沖區
◇外部接口相似:它也有一個寫入端(用於push)和?一個讀出端(用於pop),也有緩沖區“滿”和“空”的狀態。
◇內部結構迥異:
技術分享

從上圖可以看出,環形緩沖區所有的push和pop操作都是在一個固定的存儲空間內進行。而隊列緩沖區在push的時候,可能會分配存儲空間用於存儲新元素;在pop時,可能會釋放廢棄元素的存儲空間。
★環形緩沖區的實現
◇數組方式 和鏈表方式
◇讀寫操作
◇判斷“空”和“滿”
★應用場合
◇用於並發線程
◇用於並發進程

生產者、消費者模型