【摘要】通過前兩篇博文的介紹,相信讀者一定對生成器有了基本的掌握。在這篇博文中我將介紹生產者消費者模型是什麼,並用生成器實現這個模型,相當於補充強化,也算是回顧一下《操作系統》中生產者消費者模型的內容。
1. 生產者消費者模型的簡介
生產者消費者模型當中有兩大類重要的角色,一個是生產者(負責造數據的任務),另一個是消費者(接收造出來的數據進行進一步的操作)。
1. 爲什麼要使用生產者消費者模型?
在併發編程中,如果生產者處理速度很快,而消費者處理速度比較慢,那麼生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大於生產者,那麼消費者就必須等待生產者。爲了解決這個等待的問題,就引入了生產者與消費者模型。讓它們之間可以不停的生產和消費。
2. 實現生產者消費者模型三要素:
-
生產者
-
消費者
-
隊列(或其他的容器,但隊列不用考慮鎖的問題)
3. 什麼時候用這個模型?
程序中出現明顯的兩類任務,一類任務是負責生產,另外一類任務是負責處理生產的數據的(如爬蟲)
4. 用該模型的好處?
-
實現了生產者與消費者的解耦和
-
平衡了生產力與消費力,就是生產者一直不停的生產,消費者可以不停的消費,因爲二者不再是直接溝通的,而是跟隊列溝通的。
2.用生成器實現生產者消費者模型
這裏我們擬實現一個蘋果公司生產手機以及消費者購買手機的過程,這個模型的三要素是生產者、消費者以及緩衝區(我們選用隊列,因爲隊列的工作方式是先進先出)
隊列的實現選用從後面進,前面出。即
先進: list.append(element)
先出: element = list.pop(0)
當然也可以選用從前面僅,後面出,都可以。
先進:list.insert(0,element)
先出:element = list.pop( )
判斷緩衝區是否已滿:
import time
import random
cacheList = []
# # 設置緩衝區的最大長度, 當緩衝區到達最大長度, 那麼生產者就不能再生產了
cacheListLen = 2
def is_full():
"""
判斷緩衝區隊列是否已經滿了
:return:
"""
return len(cacheList) == cacheListLen
生產者:
def Producer(name):
"""
生產者, 主要用於生產數據
:return:
"""
while True:
if not is_full():
print("生產者[%s]正在生產蘋果手機....." %(name))
# 模擬生產者生產數據需要的時間, 隨機休眠0~1秒,
time.sleep(random.random())
print("[%s] 已經生產蘋果手機完成" %(name))
# 將生產的遊戲機放入緩衝區
cacheList.append('蘋果手機')
else:
print("緩存已滿!請停止生產")
yield
消費者:
def Consumer(name):
"""
消費者, 用於處理/消費數據
:return:
"""
print("【%s】正在準備購買蘋果手機" %(name))
while True:
item = yield
print("【%s】購買%s成功" %(name,item))
調用代碼:
# producer是一個生成器
producer = Producer("蘋果公司")
next(producer)
# 列表生成式, 生成消費者
consumers = [Consumer("消費者%s" %(i+1)) for i in range(3)]
# 依次遍歷所有的消費者, 給提供手機
for consumer in consumers:
if not cacheList:
print("目前商店沒有手機庫存.....")
else:
#如果緩衝區不滿,先生產,再提供消費
if not is_full():
next(producer)
item = cacheList.pop(0)
next(consumer)
consumer.send(item)
else:
item = cacheList.pop(0)
next(consumer)
consumer.send(item)
執行結果如下:
理解代碼執行流程:
-
producer = Producer(“蘋果公司”)
調用生產者Producer這個函數,得到一個producer生成器 -
next(producer)
通過next調用這個生成器,直到yeild關鍵字出現,停止執行代碼
進入到producer這個生成器代碼中,首先執行if not is_full( ),判斷緩衝區是否已滿,如果沒有滿就一直生產;滿了之後,打印“緩存已滿!請停止生產”,並停止到yield關鍵字處。 -
consumers = [Consumer(“消費者%s” %(i+1)) for i in range(3)]
這是一個列表生成式,調用三次消費者Consumer這個函數,得到三個生成器以列表的形式存儲在consumers列表中。這裏也可以使用(),生成一個consumers生成器,裏面存放三個生成器。兩種方法都是可以的 -
for consumer in consumers
使用for循環遍歷這個列表,依次拿到這三個消費者生成器。
每拿到一個,先判斷緩衝器是否還有庫存,如果沒有庫存,打印“目前商店沒有手機庫存…”;
如果有庫存,先判斷緩衝區是否已滿,如果不滿,先next(producer),執行生產者這個生成器,緩衝區滿後遇到yield停止返回調用代碼,再執行item = cacheList.pop(0),拿出緩衝區(隊列中)的第一個元素,再next(consumer)調用消費者的生成器,打印“消費者正在準備購買蘋果手機”,進入死循環,遇到yield停止執行,返回調用代碼。再執行consumer.send(item),將緩衝區拿到的第一個元素通過send方法,傳遞給consumer這個生成器的上一個yield關鍵字之前的變量item,執行print("【%s】購買%s成功" %(name,item))
這個代碼還存在很多不足之處,但是重點是理解生產者消費者模型,以及理解這個代碼的流程。