【Python之旅】第六篇(五):生產者消費者模型實現多線程異步交互

    雖然標題是“生產者消費者模型實現多線程異步交互”,但這裏要說的應該還包括Python的消息隊列,因爲這裏多線程異步交互是通過Python的消息隊列來實現的,因此主要內容如下:

1.生產者消費者模型:廚師做包子與顧客吃包子
2.Python的消息隊列
3.利用消息隊列實現Python多線程異步交互
4.再談耦合度的問題


1.生產者消費者模型

    通過廚師做包子與顧客吃包子來引出生產者消費者模型,如下圖:

wKioL1YaZuXD7ML-AABsJiq32LM738.jpg


    這裏,廚師相當於生產者,顧客相當於消費者,顧客吃包子,廚師做包子。做一個假設,如果廚師做包子的速度遠遠比顧客吃包子的速度要快,有這樣一種情況,廚師等顧客吃完一個包子後再做下一個包子(這時我們說廚師與顧客的耦合度高,即廚師做包子與顧客吃包子是緊密相連的),這樣顯然效率會很低,現實情況中也不可能是這樣,因爲一旦顧客多時,廚師就有可能忙不過來了。

    可以嘗試這樣解決:不管顧客有沒有吃完包子,廚師也繼續做包子,但此時是先把包子放在一個包子櫃檯中,等顧客有需要時再去包子櫃檯拿包子。如此一來,廚師和顧客的耦合度就變低了,即廚師做包子並不取決於顧客是否把包子吃完,這樣的話效率顯然就會高多。


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()

    程序執行結果如下:

wKiom1YabuuxTmHHAAP7wIDR7v4163.jpg

wKioL1YabwrgL24XAANaB-1ZI3c686.jpg

    利用這個程序,很好地模擬了前面“廚師做包子顧客吃包子”的例子,而從程序的執行結果中也可以看出,線程的執行是異步的,儘管如此,數據還是進行了交互,作用是:在多線程和多線程之間進行數據交互的時候,不會出現數據的阻塞。

    基於前面3點,對Python多線程異步交互應該是有一個比較好的理解了。


4.再談耦合度的問題

    繼續前面“廚師做包子與顧客吃包子”的問題,有一個問題,如果包子的製作是兩個廚師完成,即一個做餡,一個包包子,那麼正常情況下,兩個人的工作需要串行完成才能做完一個包子,即這兩個人的耦合度非常高,也就是說他們的工作聯繫緊密,在這種情況下,我們應該儘可能地減少這種耦合度,這時,只需要在兩個廚師之間再加一個隊列,即廚師A把餡做好後就放在隊列中,廚師B根據自己做包子的快慢去取餡,這樣就不會出現廚師A非得等廚師B把一個包子包完了再去做另一個包子的餡,廚師B也不一定需要先等廚師A把手頭上的餡做好纔去包包子,因爲這時隊列中已經有餡了,如此因爲,再根據兩個兩項工作速度的不同,給廚師A或廚師B再多增加一名或多名助手,就可以大大地增加整一個做包子的速度了。

    運用在寫程序的過程中,也應該要這樣去思考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章