生产者消费者模型

生产者消费者模型

一、抢票小程序

from multiprocessing import Process,Lock
import time
import json
import random
import os


#查看车票子进程
def seach():
    time.sleep(random.randint(0,3))  #模拟网络IO
    with open('db.txt') as f:   #因为是查看,默认查看,所以就不用写了
        msg = json.load(f)  #将文件中的json数据读为python数据
        print(f'余票{msg["count"]}张')

#买票子进程
def get():
    #先查看票数
    time.sleep(random.randint(0, 3))  # 模拟读数据的网络IO
    with open('db.txt') as f:
        msg = json.load(f)
        print(f'余票{msg["count"]}张')

    if msg["count"] > 0 :
        msg["count"] -= 1
        time.sleep(random.randint(0, 2))  # 模拟写数据的网络IO
        with open('db.txt','w',encoding='utf8') as f:  #把修改后的数据写进去
            json.dump(msg,f)  #将Python数据以json形式写去文件
            print(f'进程{os.getpid()}抢票成功')
    else:
        print('票售空了')

#调动函数子程序
def Foo(l):   #加锁让程序变为了串行,牺牲了效率,但是保护了数据安全
    seach()
    l.acquire()  #拿钥匙
    get()
    l.release()  #还钥匙

if __name__ == '__main__':
    for i in range(4):
        l = Lock()  #实例化一个锁对象  写在主进程是为了让子进程拿到同一把锁
        p = Process(target=Foo,args=(l,))
        p.start()
        # p.join()  #就这一句话,会把他搞成串行

二、队列

管道(pipe):基于共享的内存空间

队列(Queue):管道+锁

from multiprocessing import Queue

q = Queue(2)   #实例化一个队列对象,队列里面最多放四个
q.put('hahhahahah')   #往队列中放东西
q.put(1)   #往队列中放东西
q.put([1,2,3,4])  #队列满了的情况下再放值就会阻塞

q.get()   #从队列中取东西
q.get()   #从队列中取东西
q.get()   #放几个,拿几个,如果没有可以拿的值,默认就会一直等着拿值(阻塞)

q = Queue(2)   #实例化一个队列对象,队列里面最多放四个
q.put('yjy',block=True,timeout=1)   #block=True默认阻塞
q.put('yjy',block=True,timeout=1)   #timeout=1最多等待一秒,如果一秒时队列还哦是满的就报错
#第三次报错 :queue.Full

q.get(block=True,timeout=2)  #block=True如果队列中空了,会阻塞等待
q.get(block=True,timeout=2)  #timeout=2,等待两秒,取不到就会报错:queue.Empty
'''
在put()和get()中,如果block=False,就不会阻塞,不用等,直接报错
'''

q.put_nowait()   #相当于block=False
q.get_nowait()   #相当于block=False

三、生产者消费者模型

生产者可以不停地生产数据,达到自己最大的生产效率

消费者可以不停地处理数据,达到自己的最大处理效率

生产者消费者模型,提高了生产者生产的效率和消费者处理数据的效率

from multiprocessing import Process,Queue
import time
import random

'''生产者进程'''
def producer(name,food,q):
    for i in range(2):
        time.sleep(random.randint(1,3))
        res = f"{food}{i}"
        q.put(res)
        print(f'\033[44m{name} 生产了 {res}\033[0m')



'''消费者进程'''
def consumer(name,q):
    while True:
        res = q.get()
        if res is None:
            break   #收到为空的信号就结束
        time.sleep(random.randint(1,3))
        print(f"{name}吃了{res}")

if __name__ == '__main__':
    q = Queue()
    #生产者
    p1 = Process(target=producer,args=('yjy','酱大骨',q))
    p2 = Process(target=producer,args=('wwb','酸菜鱼',q))
    p3 = Process(target=producer,args=('hhh','卤猪蹄',q))

    #消费者
    c1 = Process(target=consumer,args = ('xx',q,))
    c2 = Process(target=consumer,args = ('yy',q,))

    #开始生产,开始吃
    p1.start()
    p2.start()
    p3.start()
    c1.start()
    c2.start()

    #必须保证生产者生产完才能发送结束的信号,运用到.join
    p1.join()  #感知子进程的结束
    p2.join()
    p3.join()

    #有几个消费者就发送几次信号None
    q.put(None)
    q.put(None) #发送结束信号
    print("主进程结束了")

补充: queue不适合传大文件,通常传一些消息.

四、JoinableQueue实现生产者消费者模型

4.1方法介绍

JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有以下方法:

q.task_done()
使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。

q.join()
生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。

4.2 模型

from multiprocessing import Process,JoinableQueue
import time
import random

'''生产者进程'''
def producer(name,food,q):
    for i in range(2):
        time.sleep(random.randint(1,3))
        res = f"{food}{i}"
        q.put(res)
        print(f'\033[44m{name} 生产了 {res}\033[0m')
    q.join()  #生产完毕,使用此方法进行阻塞,直到队列中所有项目均被处理。



'''消费者进程'''
def consumer(name,q):
    while True:
        res = q.get()
        time.sleep(random.randint(1,3))
        print(f"{name}吃了{res}")
        q.task_done()  #向q.join()发送一次信号,证明一个数据已经被取走了

if __name__ == '__main__':
    q = JoinableQueue()
    #生产者
    p1 = Process(target=producer,args=('yjy','酱大骨',q))
    p2 = Process(target=producer,args=('wwb','酸菜鱼',q))
    p3 = Process(target=producer,args=('hhh','卤猪蹄',q))

    #消费者
    c1 = Process(target=consumer,args = ('xx',q,))
    c2 = Process(target=consumer,args = ('yy',q,))
    c1.daemon = True  #将他设置为守护进程
    c2.daemon = True  #将他设置为守护进程

    #开始生产,开始吃
    l = [p1,p2,p3,c1,c2]
    for i in l :
        i.start()

    #必须保证生产者生产完才能发送结束的信号,运用到.join
    p1.join()  #感知子进程的结束
    p2.join()
    p3.join()
    print("主进程结束了")

'''
主进程等p1,p2,p3
P1,P2,p3等c,c2
p1,p2,p3结束了,证明c1,c2肯定全都收完了p1,p2,p3发到队列的数据
#因而c1,c2也没有存在的价值了,不需要继续阻塞在进程中影响主进程了。应该随着主进程的结束而结束,所以设置成守护进程就可以了。
'''
posted @ 2019-09-16 17:20  JIAYIYAN  阅读(167)  评论(0编辑  收藏  举报