【python】生成器案例之生產者消費者模型

【摘要】通過前兩篇博文的介紹,相信讀者一定對生成器有了基本的掌握。在這篇博文中我將介紹生產者消費者模型是什麼,並用生成器實現這個模型,相當於補充強化,也算是回顧一下《操作系統》中生產者消費者模型的內容。

1. 生產者消費者模型的簡介

生產者消費者模型當中有兩大類重要的角色,一個是生產者(負責造數據的任務),另一個是消費者(接收造出來的數據進行進一步的操作)。
在這裏插入圖片描述

1. 爲什麼要使用生產者消費者模型?
在併發編程中,如果生產者處理速度很快,而消費者處理速度比較慢,那麼生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大於生產者,那麼消費者就必須等待生產者。爲了解決這個等待的問題,就引入了生產者與消費者模型。讓它們之間可以不停的生產和消費。

2. 實現生產者消費者模型三要素:

  1. 生產者

  2. 消費者

  3. 隊列(或其他的容器,但隊列不用考慮鎖的問題)

3. 什麼時候用這個模型?

程序中出現明顯的兩類任務,一類任務是負責生產,另外一類任務是負責處理生產的數據的(如爬蟲)

4. 用該模型的好處?

  1. 實現了生產者與消費者的解耦和

  2. 平衡了生產力與消費力,就是生產者一直不停的生產,消費者可以不停的消費,因爲二者不再是直接溝通的,而是跟隊列溝通的。

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

這個代碼還存在很多不足之處,但是重點是理解生產者消費者模型,以及理解這個代碼的流程。

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