架構(gòu)設(shè)計(jì):生產(chǎn)者/消費(fèi)者模式

概述

今天打算來介紹一下“生產(chǎn)者/消費(fèi)者模式”,這玩意兒在很多開發(fā)領(lǐng)域都能派上用場。
看到這里,可能有同學(xué)心中犯嘀咕了:在四人幫(GOF)的23種模式里面似乎沒聽說過這種嘛!其實(shí)GOF那經(jīng)典的23種模式主要是基于OO的(從書名《Design Patterns: Elements of Reusable Object-Oriented Software》就可以看出來)。而Pattern實(shí)際上即可以是OO的Pattern,也可以是非OO的Pattern的。

常見場景

某個(gè)模塊負(fù)責(zé)產(chǎn)生數(shù)據(jù),這些數(shù)據(jù)由另一個(gè)模塊來負(fù)責(zé)處理。產(chǎn)生數(shù)據(jù)的模塊,就形象地稱為生產(chǎn)者;而處理數(shù)據(jù)的模塊,就稱為消費(fèi)者。
該模式還需要有一個(gè)緩沖區(qū)處于生產(chǎn)者和消費(fèi)者之間,作為一個(gè)中介。生產(chǎn)者把數(shù)據(jù)放入緩沖區(qū),而消費(fèi)者從緩沖區(qū)取出數(shù)據(jù)

緩沖區(qū)作用

  1. 解耦,生產(chǎn)者和消費(fèi)者只依賴緩沖區(qū),而不互相依賴;
  2. 支持并發(fā)和異步。

簡介

言歸正傳!在實(shí)際的軟件開發(fā)過程中,經(jīng)常會(huì)碰到如下場景:某個(gè)模塊負(fù)責(zé)產(chǎn)生數(shù)據(jù),這些數(shù)據(jù)由另一個(gè)模塊來負(fù)責(zé)處理(此處的模塊是廣義的,可以是類、函數(shù)、線程、進(jìn)程等)。產(chǎn)生數(shù)據(jù)的模塊,就形象地稱為生產(chǎn)者;而處理數(shù)據(jù)的模塊,就稱為消費(fèi)者。

單單抽象出生產(chǎn)者和消費(fèi)者,還夠不上是生產(chǎn)者/消費(fèi)者模式。該模式還需要有一個(gè)緩沖區(qū)處于生產(chǎn)者和消費(fèi)者之間,作為一個(gè)中介。生產(chǎn)者把數(shù)據(jù)放入緩沖區(qū),而消費(fèi)者從緩沖區(qū)取出數(shù)據(jù)。大概的結(jié)構(gòu)如下圖。

為了不至于太抽象,我們舉一個(gè)寄信的例子(雖說這年頭寄信已經(jīng)不時(shí)興,但這個(gè)例子還是比較貼切的)。假設(shè)你要寄一封平信,大致過程如下:

  1. 你把信寫好——相當(dāng)于生產(chǎn)者制造數(shù)據(jù)
  2. 你把信放入郵筒——相當(dāng)于生產(chǎn)者把數(shù)據(jù)放入緩沖區(qū)
  3. 郵遞員把信從郵筒取出——相當(dāng)于消費(fèi)者把數(shù)據(jù)取出緩沖區(qū)
  4. 郵遞員把信拿去郵局做相應(yīng)的處理——相當(dāng)于消費(fèi)者處理數(shù)據(jù)

生產(chǎn)消費(fèi)者模型

生產(chǎn)者消費(fèi)者模型具體來講,就是在一個(gè)系統(tǒng)中,存在生產(chǎn)者和消費(fèi)者兩種角色,他們通過內(nèi)存緩沖區(qū)進(jìn)行通信,生產(chǎn)者生產(chǎn)消費(fèi)者需要的資料,消費(fèi)者把資料做成產(chǎn)品。生產(chǎn)消費(fèi)者模式如下圖。

生產(chǎn)消費(fèi)者模型

在日益發(fā)展的服務(wù)類型中,譬如注冊用戶這種服務(wù),它可能解耦成好幾種獨(dú)立的服務(wù)(賬號驗(yàn)證,郵箱驗(yàn)證碼,手機(jī)短信碼等)。它們作為消費(fèi)者,等待用戶輸入數(shù)據(jù),在前臺數(shù)據(jù)提交之后會(huì)經(jīng)過分解并發(fā)送到各個(gè)服務(wù)所在的url,分發(fā)的那個(gè)角色就相當(dāng)于生產(chǎn)者。消費(fèi)者在獲取數(shù)據(jù)時(shí)候有可能一次不能處理完,那么它們各自有一個(gè)請求隊(duì)列,那就是內(nèi)存緩沖區(qū)了。做這項(xiàng)工作的框架叫做消息隊(duì)列。

優(yōu)點(diǎn)

可能有同學(xué)會(huì)問了:這個(gè)緩沖區(qū)有什么用捏?為什么不讓生產(chǎn)者直接調(diào)用消費(fèi)者的某個(gè)函數(shù),直接把數(shù)據(jù)傳遞過去?搞出這么一個(gè)緩沖區(qū)作甚?
其實(shí)這里面是大有講究的,大概有如下一些好處。

  • 解耦

假設(shè)生產(chǎn)者和消費(fèi)者分別是兩個(gè)類。如果讓生產(chǎn)者直接調(diào)用消費(fèi)者的某個(gè)方法,那么生產(chǎn)者對于消費(fèi)者就會(huì)產(chǎn)生依賴(也就是耦合)。將來如果消費(fèi)者的代碼發(fā)生變化,可能會(huì)影響到生產(chǎn)者。而如果兩者都依賴于某個(gè)緩沖區(qū),兩者之間不直接依賴,耦合也就相應(yīng)降低了。

接著上述的例子,如果不使用郵筒(也就是緩沖區(qū)),你必須得把信直接交給郵遞員。有同學(xué)會(huì)說,直接給郵遞員不是挺簡單的嘛?其實(shí)不簡單,你必須得認(rèn)識誰是郵遞員,才能把信給他(光憑身上穿的制服,萬一有人假冒,就慘了)。這就產(chǎn)生和你和郵遞員之間的依賴(相當(dāng)于生產(chǎn)者和消費(fèi)者的強(qiáng)耦合)。萬一哪天郵遞員換人了,你還要重新認(rèn)識一下(相當(dāng)于消費(fèi)者變化導(dǎo)致修改生產(chǎn)者代碼)。而郵筒相對來說比較固定,你依賴它的成本就比較低(相當(dāng)于和緩沖區(qū)之間的弱耦合)。

  • 支持并發(fā)(concurrency)

生產(chǎn)者直接調(diào)用消費(fèi)者的某個(gè)方法,還有另一個(gè)弊端。由于函數(shù)調(diào)用是同步的(或者叫阻塞的),在消費(fèi)者的方法沒有返回之前,生產(chǎn)者只好一直等在那邊。萬一消費(fèi)者處理數(shù)據(jù)很慢,生產(chǎn)者就會(huì)白白糟蹋大好時(shí)光。

使用了生產(chǎn)者/消費(fèi)者模式之后,生產(chǎn)者和消費(fèi)者可以是兩個(gè)獨(dú)立的并發(fā)主體(常見并發(fā)類型有進(jìn)程和線程兩種,后面的帖子會(huì)講兩種并發(fā)類型下的應(yīng)用)。生產(chǎn)者把制造出來的數(shù)據(jù)往緩沖區(qū)一丟,就可以再去生產(chǎn)下一個(gè)數(shù)據(jù)?;旧喜挥靡蕾囅M(fèi)者的處理速度。

其實(shí)當(dāng)初這個(gè)模式,主要就是用來處理并發(fā)問題的。

從寄信的例子來看。如果沒有郵筒,你得拿著信傻站在路口等郵遞員過來收(相當(dāng)于生產(chǎn)者阻塞);又或者郵遞員得挨家挨戶問,誰要寄信(相當(dāng)于消費(fèi)者輪詢)。不管是哪種方法,都挺土的。

  • 支持忙閑不均

緩沖區(qū)還有另一個(gè)好處。如果制造數(shù)據(jù)的速度時(shí)快時(shí)慢,緩沖區(qū)的好處就體現(xiàn)出來了。當(dāng)數(shù)據(jù)制造快的時(shí)候,消費(fèi)者來不及處理,未處理的數(shù)據(jù)可以暫存在緩沖區(qū)中。等生產(chǎn)者的制造速度慢下來,消費(fèi)者再慢慢處理掉。

為了充分復(fù)用,我們再拿寄信的例子來說事。假設(shè)郵遞員一次只能帶走1000封信。萬一某次碰上情人節(jié)(也可能是圣誕節(jié))送賀卡,需要寄出去的信超過1000封,這時(shí)候郵筒這個(gè)緩沖區(qū)就派上用場了。郵遞員把來不及帶走的信暫存在郵筒中,等下次過來時(shí)再拿走。

如何確定數(shù)據(jù)單元?

既然前一個(gè)帖子已經(jīng)搞過掃盲了,那接下來應(yīng)該開始聊一些具體的編程技術(shù)問題了。不過在進(jìn)入具體的技術(shù)細(xì)節(jié)之前,咱們先要搞明白一個(gè)問題:如何確定數(shù)據(jù)單元?只有把數(shù)據(jù)單元分析清楚,后面的技術(shù)設(shè)計(jì)才好搞。

啥是數(shù)據(jù)單元

何謂數(shù)據(jù)單元捏?簡單地說,每次生產(chǎn)者放到緩沖區(qū)的,就是一個(gè)數(shù)據(jù)單元;每次消費(fèi)者從緩沖區(qū)取出的,也是一個(gè)數(shù)據(jù)單元。對于前一個(gè)帖子中寄信的例子,我們可以把每一封單獨(dú)的信件看成是一個(gè)數(shù)據(jù)單元。
不過光這么介紹,太過于簡單,無助于大伙兒分析出這玩意兒。所以,后面咱們來看一下數(shù)據(jù)單元需要具備哪些特性。搞明白這些特性之后,就容易從復(fù)雜的業(yè)務(wù)邏輯中分析出適合做數(shù)據(jù)單元的東西了。

數(shù)據(jù)單元的特性

分析數(shù)據(jù)單元,需要考慮如下幾個(gè)方面的特性:

  • 關(guān)聯(lián)到業(yè)務(wù)對象

首先,數(shù)據(jù)單元必須關(guān)聯(lián)到某種業(yè)務(wù)對象。在考慮該問題的時(shí)候,你必須深刻理解當(dāng)前這個(gè)生產(chǎn)者/消費(fèi)者模式所對應(yīng)的業(yè)務(wù)邏輯,才能夠作出合適的判斷。
由于“寄信”這個(gè)業(yè)務(wù)邏輯比較簡單,所以大伙兒很容易就可以判斷出數(shù)據(jù)單元是啥。但現(xiàn)實(shí)生活中,往往沒這么樂觀。大多數(shù)業(yè)務(wù)邏輯都比較復(fù)雜,當(dāng)中包含的業(yè)務(wù)對象是層次繁多、類型各異。在這種情況下,就不易作出決策了。
這一步很重要,如果選錯(cuò)了業(yè)務(wù)對象,會(huì)導(dǎo)致后續(xù)程序設(shè)計(jì)和編碼實(shí)現(xiàn)的復(fù)雜度大為上升,增加了開發(fā)和維護(hù)成本。

  • 完整性

所謂完整性,就是在傳輸過程中,要保證該數(shù)據(jù)單元的完整。要么整個(gè)數(shù)據(jù)單元被傳遞到消費(fèi)者,要么完全沒有傳遞到消費(fèi)者。不允許出現(xiàn)部分傳遞的情形。
對于寄信來說,你不能把半封信放入郵筒;同樣的,郵遞員從郵筒中拿信,也不能只拿出信的一部分。

  • 獨(dú)立性

所謂獨(dú)立性,就是各個(gè)數(shù)據(jù)單元之間沒有互相依賴,某個(gè)數(shù)據(jù)單元傳輸失敗不應(yīng)該影響已經(jīng)完成傳輸?shù)膯卧灰膊粦?yīng)該影響尚未傳輸?shù)膯卧?br> 為啥會(huì)出現(xiàn)傳輸失敗捏?假如生產(chǎn)者的生產(chǎn)速度在一段時(shí)間內(nèi)一直超過消費(fèi)者的處理速度,那就會(huì)導(dǎo)致緩沖區(qū)不斷增長并達(dá)到上限,之后的數(shù)據(jù)單元就會(huì)被丟棄。如果數(shù)據(jù)單元相互獨(dú)立,等到生產(chǎn)者的速度降下來之后,后續(xù)的數(shù)據(jù)單元繼續(xù)處理,不會(huì)受到牽連;反之,如果數(shù)據(jù)單元之間有某種耦合,導(dǎo)致被丟棄的數(shù)據(jù)單元會(huì)影響到后續(xù)其它單元的處理,那就會(huì)使程序邏輯變得非常復(fù)雜。
對于寄信來說,某封信弄丟了,不會(huì)影響后續(xù)信件的送達(dá);當(dāng)然更不會(huì)影響已經(jīng)送達(dá)的信件。

  • 顆粒度

前面提到,數(shù)據(jù)單元需要關(guān)聯(lián)到某種業(yè)務(wù)對象。那么數(shù)據(jù)單元和業(yè)務(wù)對象是否要一一對應(yīng)捏?很多場合確實(shí)是一一對應(yīng)的。
不過,有時(shí)出于性能等因素的考慮,也可能會(huì)把N個(gè)業(yè)務(wù)對象打包成一個(gè)數(shù)據(jù)單元。那么,這個(gè)N該如何取值就是顆粒度的考慮了。顆粒度的大小是有講究的。太大的顆粒度可能會(huì)造成某種浪費(fèi);太小的顆粒度可能會(huì)造成性能問題。顆粒度的權(quán)衡要基于多方面的因素,以及一些經(jīng)驗(yàn)值的考量。
還是拿寄信的例子。如果顆粒度過小(比如設(shè)定為1),那郵遞員每次只取出1封信。如果信件多了,那就得來回跑好多趟,浪費(fèi)了時(shí)間。
如果顆粒度太大(比如設(shè)定為100),那寄信的人得等到湊滿100封信才拿去放入郵筒。假如平時(shí)很少寫信,就得等上很久,也不太爽。
可能有同學(xué)會(huì)問:生產(chǎn)者和消費(fèi)者的顆粒度能否設(shè)置成不同大?。ū热鐚τ诩男湃嗽O(shè)置成1,對于郵遞員設(shè)置成100)。當(dāng)然,理論上可以這么干,但是在某些情況下會(huì)增加程序邏輯和代碼實(shí)現(xiàn)的復(fù)雜度。后面討論具體技術(shù)細(xì)節(jié)時(shí),或許會(huì)聊到這個(gè)問題。
好,數(shù)據(jù)單元的話題就說到這。希望通過本帖子,大伙兒能夠搞明白數(shù)據(jù)單元到底是怎么一回事。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 簡介 在實(shí)際的軟件開發(fā)過程中,經(jīng)常會(huì)碰到如下場景: 某個(gè)模塊負(fù)責(zé)產(chǎn)生數(shù)據(jù),這些數(shù)據(jù)由另一個(gè)模塊來負(fù)責(zé)處理(此處的模...
    RadioWaves閱讀 6,484評論 2 16
  • 什么是生產(chǎn)者消費(fèi)者模式 在軟件開發(fā)的過程中,經(jīng)常碰到這樣的場景:某些模塊負(fù)責(zé)生產(chǎn)數(shù)據(jù),這些數(shù)據(jù)由其他模塊來負(fù)責(zé)處理...
    老__鷹閱讀 1,136評論 0 6
  • 什么是生產(chǎn)者消費(fèi)者模式 在軟件開發(fā)的過程中,經(jīng)常碰到這樣的場景: 某些模塊負(fù)責(zé)生產(chǎn)數(shù)據(jù),這些數(shù)據(jù)由其他模塊來負(fù)責(zé)處...
    chen_000閱讀 432評論 0 0
  • 減速帶能夠有效減少交通要道口事故的發(fā)生,在學(xué)校路口、住宅小區(qū)路口、商場路口、酒店路口等一些路口會(huì)裝置各式各樣的減速...
    未來神話閱讀 285評論 0 0
  • 讀完尹建莉《好媽媽勝過好老師2自由的孩子最自覺》這本書,結(jié)合自己四年的育兒經(jīng)驗(yàn),讓我對“自由”的理解更深刻...
    鄭鄭老三閱讀 676評論 1 1

友情鏈接更多精彩內(nèi)容