雖然標題是“生產者消費者模型實現多線程異步交互”,但這裏要說的應該還包括Python的消息隊列,因爲這裏多線程異步交互是通過Python的消息隊列來實現的,因此主要內容如下:
1.生產者消費者模型:廚師做包子與顧客吃包子 2.Python的消息隊列 3.利用消息隊列實現Python多線程異步交互 4.再談耦合度的問題
1.生產者消費者模型
通過廚師做包子與顧客吃包子來引出生產者消費者模型,如下圖:
這裏,廚師相當於生產者,顧客相當於消費者,顧客吃包子,廚師做包子。做一個假設,如果廚師做包子的速度遠遠比顧客吃包子的速度要快,有這樣一種情況,廚師等顧客吃完一個包子後再做下一個包子(這時我們說廚師與顧客的耦合度高,即廚師做包子與顧客吃包子是緊密相連的),這樣顯然效率會很低,現實情況中也不可能是這樣,因爲一旦顧客多時,廚師就有可能忙不過來了。
可以嘗試這樣解決:不管顧客有沒有吃完包子,廚師也繼續做包子,但此時是先把包子放在一個包子櫃檯中,等顧客有需要時再去包子櫃檯拿包子。如此一來,廚師和顧客的耦合度就變低了,即廚師做包子並不取決於顧客是否把包子吃完,這樣的話效率顯然就會高多。
2.Python中的消息隊列
通過上面的例子中,就可以引出消息隊列了:包子櫃檯即相當於Python中的消息隊列(當然不只有Python纔有消息隊列)。根據上面的例子,我們做下面的類比分析。
類比分析1:廚師和包子相當於是兩個線程(假設線程A和線程B),廚師做的包子即相當於是線程A執行後的結果,而線程B的執行需要利用線程A的執行結果,並且,線程A的執行速度比線程B的執行速度要快。
類比分析2:廚師不會等顧客吃完包子後再做下一個包子,即線程A也不會等線程B使用線程A的執行結果後再去執行下一次功能相同的線程A2,否則程序運行效率會很低。
類比分析3:廚師把做好的包子放在包子櫃檯裏,顧客吃完一個包子後再去包子櫃檯取,線程A把執行結果存放在消息隊列中,然後再執行下一個功能相同的線程A2,線程B在消息隊列中取勝線程A的執行結果,然後再執行下一個功能相同的線程B2,如此類推。
通過上面的分析,我們就可以知道,通過使用消息隊列,我們就可以降低兩個線程之間的耦合度,這樣就可以提高程序的運行效率。
3.利用消息隊列實現Python多線程異步交互
上面的例子,線程A和線程B的執行速度是不一樣的(異步),但卻線程B卻需要使用線程A的執行結果(交互),通過使用Python的消息隊列,就可以實現線程的異步交互。
程序代碼如下:
#!/usr/bin/env python import threading, time import Queue #導入消息隊列模塊 import random #導入隨機數模塊,是爲了模擬生產者與消費者速度不一致的情形 q = Queue.Queue() #實例化一個對象 def Producer(name): #生產者函數 for i in range(20): q.put(i) #將結果放入消息隊列中 print '\033[32;1mProducer %s has made %s baozi....\033[0m' % (name, i) time.sleep(random.randrange(3)) #生產者的生產速度,3s內 def Consumer(name): #消費者函數 count = 0 while count < 20: data = q.get() #取用消息隊列中存放的結果 print '\033[31;1mConsumer %s has eatem %s baozi...chihuo...\033[0m' % (name, data) count += 1 time.sleep(random.randrange(4)) #消費者的消費速度,4s內 p = threading.Thread(target = Producer, args = ('Alex',)) c = threading.Thread(target = Consumer, args = ('Wangfanhao',)) p.start() c.start()
程序執行結果如下:
利用這個程序,很好地模擬了前面“廚師做包子顧客吃包子”的例子,而從程序的執行結果中也可以看出,線程的執行是異步的,儘管如此,數據還是進行了交互,作用是:在多線程和多線程之間進行數據交互的時候,不會出現數據的阻塞。
基於前面3點,對Python多線程異步交互應該是有一個比較好的理解了。
4.再談耦合度的問題
繼續前面“廚師做包子與顧客吃包子”的問題,有一個問題,如果包子的製作是兩個廚師完成,即一個做餡,一個包包子,那麼正常情況下,兩個人的工作需要串行完成才能做完一個包子,即這兩個人的耦合度非常高,也就是說他們的工作聯繫緊密,在這種情況下,我們應該儘可能地減少這種耦合度,這時,只需要在兩個廚師之間再加一個隊列,即廚師A把餡做好後就放在隊列中,廚師B根據自己做包子的快慢去取餡,這樣就不會出現廚師A非得等廚師B把一個包子包完了再去做另一個包子的餡,廚師B也不一定需要先等廚師A把手頭上的餡做好纔去包包子,因爲這時隊列中已經有餡了,如此因爲,再根據兩個兩項工作速度的不同,給廚師A或廚師B再多增加一名或多名助手,就可以大大地增加整一個做包子的速度了。
運用在寫程序的過程中,也應該要這樣去思考。