day30 生产者消费者模型

生产者消费者模型

模型就是解决某个问题的固定方法或者套路。在并发编程中使用生产者和消费者模型能够解决绝大多数并发问题。该模式通过平衡生产进程和消费进程的工作能力来提高程序的整体处理数据的速度

要解决什么问题

生产者:就是生产数据的一方

消费者:就是处理数据的一方

在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。所以由于双方的处理速度不同,一个快一个慢,则双方需要相互等待效率低

具体的解决方法:

通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

1.先将双方解开耦合,让不同的进程负责不同的任务

2.提供一个共享的容器,来平衡双方的能力,之所以用进程队列是因为队列可以在进程间共享

基于队列实现生产者消费者模型

import os
import time
import random
from multiprocessing import Queue, Process


def product(q):
    for i in range(1, 6):
        time.sleep(random.random())
        q.put(i)
        print(f'{os.getpid()}生产了{i}个数据')


def customer(q):
    while True:
        res = q.get()
        time.sleep(random.random())
        print(f'处理了{res}个数据')


if __name__ == '__main__':
    q = Queue()
    p = Process(target=product, args=(q,))
    p.start()

    c = Process(target=customer, args=(q,))
    c.start()

    print('over')

此时的问题是主进程永远不会结束,原因是:生成者生产完后就结束了,但是消费者在取空了之后,则一直楚于死循环中的q.get()这一步。

基于JoinableQueue实现生产者消费者模型

import os
import time
import random
from multiprocessing import JoinableQueue, Process


def product(q):
    for i in range(1, 6):
        time.sleep(random.random())
        q.put(i)
        print(f'{os.getpid()}生产了{i}个数据')


def customer(q):
    while True:
        res = q.get()
        time.sleep(random.random())
        print(f'处理了{res}个数据')
        q.task_done()  # 向q.join()发送一次信号,标识当前任务处理完成


if __name__ == '__main__':
    q = JoinableQueue()
    p1 = Process(target=product, args=(q,))
    p1.start()
    p2 = Process(target=product, args=(q,))
    p2.start()

    c = Process(target=customer, args=(q,))
    c.daemon = True  # 可以将消费者设置为守护进程,当主进程确认任务全部完成时,可以随着主进程一起结束
    c.start()

    p1.join()
    p2.join()

    q.join()  # 等待队列中的任务被处理完毕,会阻塞知道存入的元素个数等于task_done的调用次数

主要原因是:消费者不知道什么时候结束。

解决方式:

1.先确定生产者任务完成

2.再确定生产出来的数据已经全部处理完成

导入JoinableQueue类解决。joinablequeue继承自Queue,用法与Queue一致,增加了join和task_done两个方法,join是个阻塞函数,会阻塞直到task_done的调用次数,可以用于表示队列任务处理完成

常用来做流量削峰,保证服务器不会因为高并发崩溃

posted @ 2019-07-04 21:11  Never&say&die  阅读(162)  评论(0编辑  收藏  举报