生产者和消费者问题详解
定義
生產者消費者問題(英語:Producer-consumer problem),也稱有限緩沖問題(英語:Bounded-buffer problem),是一個多線程同步問題的經典案例。該問題描述了共享固定大小緩沖區的兩個線程——即所謂的“生產者”和“消費者”——在實際運行時會發生的問題。生產者的主要作用是生成一定量的數據放到緩沖區中,然后重復此過程。與此同時,消費者也在緩沖區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩沖區滿時加入數據,消費者也不會在緩沖區中空時消耗數據。
思路
要解決該問題,就必須讓生產者在緩沖區滿時休眠(要么干脆就放棄數據),等到下次消費者消耗緩沖區中的數據的時候,生產者才能被喚醒,開始往緩沖區添加數據。同樣,也可以讓消費者在緩沖區空時進入休眠,等到生產者往緩沖區添加數據之后,再喚醒消費者。通常采用進程間通信的方法解決該問題,常用的方法有信號燈法[1]等。如果解決方法不夠完善,則容易出現死鎖的情況。出現死鎖時,兩個線程都會陷入休眠,等待對方喚醒自己。該問題也能被推廣到多個生產者和消費者的情形。
java實現
使用信號燈的算法
信號燈可以避免上述喚醒指令不起作用的情況。該方法(見下面的代碼)使用了兩個信號燈,fillCount 和 emptyCount。fillCount 用于記錄緩沖區中將被讀取的數據項數(實際上就是有多少數據項在緩沖區里),emptyCount 用于記錄緩沖區中空閑空間數。當有新數據項被放入緩沖區時,fillCount 增加,emptyCount 減少。如果在生產者嘗試減少 emptyCount 的時候發現其值為零,那么生產者就進入休眠。等到有數據項被消耗,emptyCount 增加的時候,生產者才被喚醒。消費者的行為類似。
semaphore fillCount = 0; // 生產的項目 semaphore emptyCount = BUFFER_SIZE; // 剩余空間procedure producer() {while (true) {item = produceItem();down(emptyCount);putItemIntoBuffer(item);up(fillCount);} }procedure consumer() {while (true) {down(fillCount);item = removeItemFromBuffer();up(emptyCount);consumeItem(item);} }上述方法在只有一個生產者和一個消費者時能解決問題。對于多個生產者或者多個消費者共享緩沖區的情況,該算法也會導致競爭條件,出現兩個或以上的線程同時讀或寫同一個緩沖區槽的情況。為了說明這種情況是如何發生的,可以假設 putItemIntoBuffer() 的一種可能的實現:先尋找下一個可用空槽,然后寫入數據項。下列情形是可能出現的:
兩個生產者都減少 emptyCount 的值;
某一生產者尋找到下一個可用空槽;
另一生產者也找到了下一個可用空槽,結果和上一步被找到的是同一個空槽;
兩個生產者向可用空槽寫入數據。
多個生產者和消費者的問題
為了解決這個問題,需要在保證同一時刻只有一個生產者能夠執行 putItemIntoBuffer()。也就是說,需要尋找一種方法來互斥地執行臨界區的代碼。為了達到這個目的,可引入一個二值信號燈 mutex,其值只能為 1 或者 0。如果把線程放入 down(mutex) 和 up(mutex) 之間,就可以限制只有一個線程能被執行。多生產者、消費者的解決算法如下:
semaphore mutex = 1; semaphore fillCount = 0; semaphore emptyCount = BUFFER_SIZE;procedure producer() {while (true) {item = produceItem();down(emptyCount);down(mutex);putItemIntoBuffer(item);up(mutex);up(fillCount);} } procedure consumer() {while (true) {down(fillCount);down(mutex);item = removeItemFromBuffer();up(mutex);up(emptyCount);consumeItem(item);} }c語言的經典解法
問題分析
1) 關系分析。生產者和消費者對緩沖區互斥訪問是互斥關系,同時生產者和消費者又是一個相互協作的關系,只有生產者生產之后,消費者才能消費,他們也是同步關系。
2) 整理思路。這里比較簡單,只有生產者和消費者兩個進程,正好是這兩個進程存在著互斥關系和同步關系。那么需要解決的是互斥和同步PV操作的位置。
3) 信號量設置。信號量mutex作為互斥信號量,它用于控制互斥訪問緩沖池,互斥信號量初值為1;信號量full用于記錄當前緩沖池中“滿”緩沖區數,初值為0。信號量empty 用于記錄當前緩沖池中“空”緩沖區數,初值為n。
生產者-消費者進程的描述如下:
semaphore mutex=1; //臨界區互斥信號量 semaphore empty=n; //空閑緩沖區 semaphore full=0; //緩沖區初始化為空 producer () { //生產者進程while(1){produce an item in nextp; //生產數據P(empty); //獲取空緩沖區單元P(mutex); //進入臨界區.add nextp to buffer; //將數據放入緩沖區V(mutex); //離開臨界區,釋放互斥信號量V(full); //滿緩沖區數加1} } consumer () { //消費者進程while(1){P(full); //獲取滿緩沖區單元P(mutex); // 進入臨界區remove an item from buffer; //從緩沖區中取出數據V (mutex); //離開臨界區,釋放互斥信號量V (empty) ; //空緩沖區數加1consume the item; //消費數據} }復雜的生產者和消費者問題
問題描述
桌子上有一只盤子,每次只能向其中放入一個水果。爸爸專向盤子中放蘋果,媽媽專向盤子中放橘子,兒子專等吃盤子中的橘子,女兒專等吃盤子中的蘋果。只有盤子為空時,爸爸或媽媽就可向盤子中放一個水果;僅當盤子中有自己需要的水果時,兒子或女兒
可以從盤子中取出。
問題分析
1) 關系分析。這里的關系稍復雜一些,首先由每次只能向其中放入一只水果可知爸爸和媽媽是互斥關系。爸爸和女兒、媽媽和兒子是同步關系,而且這兩對進程必須連起來,兒子和女兒之間沒有互斥和同步關系,因為他們是選擇條件執行,不可能并發,如圖2-8所示。
2) 整理思路。這里有4個進程,實際上可以抽象為兩個生產者和兩個消費者被連接到大小為1的緩沖區上。
3) 信號量設置。首先設置信號量plate為互斥信號量,表示是否允許向盤子放入水果,初值為1,表示允許放入,且只允許放入一個。信號量 apple表示盤子中是否有蘋果,初值為0,表示盤子為空,不許取,若apple=l可以取。信號量orange表示盤子中是否有橘子,初值為0,表示盤子為空,不許取,若orange=l可以取。解決該問題的代碼如下:
semaphore plate=l, apple=0, orange=0; dad() { //父親進程while (1) {prepare an apple;P(plate) ; //互斥向盤中取、放水果put the apple on the plate; //向盤中放蘋果V(apple); //允許取蘋果} } mom() { // 母親進程while(1) {prepare an orange;P(plate); //互斥向盤中取、放水果put the orange on the plate; //向盤中放橘子V(orange); //允許取橘子} } son(){ //兒子進程while(1){P(orange) ; //互斥向盤中取橘子take an orange from the plate;V(plate); //允許向盤中取、放水果eat the orange;} } daughter () { //女兒進程while(1) {P(apple); // 互斥向盤中取蘋果take an apple from the plate;V(plate); //運行向盤中取、放水果eat the apple;} }總結
以上是生活随笔為你收集整理的生产者和消费者问题详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【历史上的今天】5 月 9 日:中国黄页
- 下一篇: [Pytorch系列-28]:神经网络基