前言
最近在學習作業系統的知識,又看到了經典的併發模型,不得不說在多執行緒程式設計中,最好使用一些已經被驗證過的正確的模型,其中生產者消費者模型就是典型的成功模型,值得學習,其實之前我也寫過生產者消費者的實現,但這次我會稍微深入一些,為什麼要這樣寫,如果不這樣寫會帶來什麼樣的問題。
簡單但是有問題的例子
int buffer;
int count =0;
void
put(
int value)
intget()
上面是put()和get()函式的實現
cond_t cond;
mutex_t mutex;
void
*producer
(void
*arg)
}void
*consumer
(void
*arg)
}
這個應該是最簡單,最直接的生產者消費者實現了,但是這裡有兩個問題,讓我們來乙個乙個解決,第乙個問題就是當有超過乙個消費者的時候,這個**有個十分嚴重的問題,假設乙個消費者在緩衝區為空的時候進行消費,很顯然,這個消費者執行緒會被阻塞在pthread_cond_wait(注意阻塞的時候這個消費者是不持有鎖的),這個時候假設乙個生產者生產了資料放入緩衝,然後呼叫pthread_cond_sigal,之前阻塞的執行緒會被放入到就緒佇列中,準備執行。但是假設這個時候有個新的消費者執行緒來了,我們叫它第三者吧,由於之前的消費者還沒有從pthread_cond_wait中返回,以及之前的生產者已經完成生產,阻塞在pthread_cond_wait中(因為緩衝區已經滿了),這個時候實際上鎖是沒有被任何執行緒持有的,所以這個第三者長驅直入,直接把生產者消費的消費掉了,然後大大咧咧的跑了,這個時候之前就緒的消費者被排程,呼叫get()結果發現緩衝區為空,直接觸發了assert,導致錯誤。
引發這個問題的原因很簡單,因為在第乙個消費者被生產者喚醒之後但在其執行之前,緩衝區已經發生了變化(由於另外乙個消費者執行緒的執行)。這個實際上意味著消費者收到被喚醒的訊號僅僅意味著狀態可能發生了變化(並不代表等待的事情一定發生了)。這個實際上是對喚醒訊號語義的理解,解決這個問題的辦法也不難,**如下:
用while代替if的辦法(仍有問題)
cond_t cond;
mutex_t mutex;
void
*producer
(void
*arg)
}void
*consumer
(void
*arg)
}
用while代替if確實解決了之前的問題,但是這個實現仍然是有問題的,那麼問題在哪呢?
想象下面的場景,假設有乙個生產者執行緒p和兩個消費者執行緒c1以及c2,假設兩個消費者執行緒先執行,並且由於緩衝區為空所以都睡眠了,生產者p執行在緩衝區放入乙個值之後喚醒了乙個消費者假設是c1,之後進入睡眠,這個時候c1發現緩衝區有資料,就直接消費了,然後這個消費者呼叫了pthread_cond_signal(&cond),但是這個時候有個問題就是,應該喚醒哪個等待的執行緒呢?我們知道肯定應該喚醒生產者執行緒,但是如果它喚醒了c2(這個是絕對可能的,取決於等待佇列的實現),這個時候問題就出現了,由於c2喚醒之後發現緩衝區為空,就直接繼續呼叫pthread_cond_wait(&cond,&mutex),進入睡眠了,這個時候三個執行緒都處於睡眠的狀態,顯然這是個問題。
簡單但是正確的例子
cond_t empty, fill;
mutex_t mutex;
void
*producer
(void
*arg)
}void
*consumer
(void
*arg)
}
總結
這裡簡單說明了生產者消費者模型的幾個小細節,之前我也寫過c++中如何實現乙個生產者消費者模型,鏈結在這裡:c++生產者消費者模型實現
簡單提一句的是總是對條件變數使用while而不是if,使用while迴圈也解決了假喚醒的情況,在某些執行緒庫中,由於不同的實現,乙個訊號可能會喚醒兩個執行緒,因此再次檢查執行緒的等待條件是正確的操作。
多執行緒 生產者消費者模型
1.簡介 生產者消費者模式是通過乙個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞佇列來進行通訊,所以生產者生產完資料之後不用等待消費者處理,直接扔給阻塞佇列,消費者不找生產者要資料,而是直接從阻塞佇列裡取,阻塞佇列就相當於乙個緩衝區,平衡了生產者和消費者的處理能...
C 多執行緒 生產者與消費者模型
條件變數的提出首先要涉及乙個概念,就是生產者消費者模型 生產者消費者,是在多執行緒同步的乙個問題,兩個固定大小緩衝區的執行緒,在實際執行是會發生問題,生產者是生成資料放入緩衝區,重複過程,消費者在緩衝區取走資料。生產者消費者的模型提出了三種關係,兩種角色,乙個場所 三種關係 生產者之間的互斥關係 消...
多執行緒 生產者消費者
這個就不多說了,直接上 include include using namespace std const unsigned short size of buffer 10 緩衝區長度 unsigned short productid 0 產品號 unsigned short consumeid 0...