在線程世界?,?產者就是?產數據的線程,消費者就是消費數據的線程。在多線程開發當中,如果?產者處理速度很快,?消費者處理速度很慢,那麽?產者就必須等待消費者處理完,才能繼續?產數據。同樣的道理,如果消費者的處理能?於?產者,那麽消費者就必須等待?產者。為了解決這個問題於是引?了?產者和消費者模式。
?產者消費者模式是通過?個容器來解決?產者和消費者的強耦合問題。?產者和消費者彼此之間不直接通訊,?通過阻塞隊列來進?通訊,所以?產者?產完數據之後不?等待消費者處理,直接扔給阻塞隊列,消費者不找?產者要數據,?是直接從阻塞隊列?取,阻塞隊列就相當於?個緩沖區,平衡了?產者和消費者的處理能?。
比如,對於同時爬取多個網頁的多線程爬蟲,在某壹時刻妳可能無法保證他們在處理不同的網站,在某些時刻他們極有可能在處理相同的網站,這豈不浪費?為了解決這個問題,可以將不同網頁的url放在queue中,然後多個線程來讀取queue中的url進行解析處理,而queue只允許壹次出壹個,出壹個少壹個。相同網站上不同網頁的url通常有某種規律,比如某個字段的數字加1,這種情況完全可以用這種模式,“生產者程序”負責根據規律把完整的url制作出來,再塞進queue裏面(如果queue滿了,則等待);“消費者程序(網頁解析程序)”從queue的後面挨個取出url進行解析(如果queue裏面是空的,則等待),即使是多線程也能保證每個線程得到的是不同的url。這個過程中,生產者和消費彼此互不幹涉。
下面以實例說明如何將queue與多線程相結合形成所謂的“ 生產者+消費者 ”模式,同時解決 多線程如何退出 的問題(註意下例中是“壹個生產者+多個消費者”的形式,多生產者+多消費者的模式可在此基礎上進壹步實現):
上述程序的過程如下圖:
註意 :
(1)上述程序中生產者插入queue的時間間隔為0.1s,而消費者的取出時間間隔為2s,顯然消費速度不如生產速度,壹開始queue是空的,壹段時間後queue就變滿了,輸出結果正說明了這壹點。如果將兩個時間調換,則結果相反,queue永遠不會滿,甚至只有1個值,因為只要進去就被消費了。
(2)消費者程序是通過“while”來推動不斷執行的,何時結束?上例中通過在queue中增加None的形式告訴消費者,生產者已經結束了,消費者也可以結束了。但消費者有多個,到底由哪個消費者得到None?為解決這個問題,上例中在消費者中先判斷當前取出的是不是None,如果是,則先在queue裏插入壹個None,然後再break當前這個消費者線程,最後的結果是所有的消費者線程都退出了,但queue中還剩下None沒有被取出。因此在程序的後面增加了壹個for循環來挨個把queue中的元素取出,否則最後的q.join()將永遠阻塞,程序無法往下執行。
(3)程序中每壹個q.get()後面都跟有壹個q.task_done(),其作用是從queue中取出壹個元素就給q.join()發送壹個信息,否則q.join()將永遠處於阻塞狀態,直到所有queue元素都被取出。
多線程“生產者-消費者”模式壹般性結構圖