Python并发编程:多进程-生产者消费者模型

一 生产者消费者模型介绍

为什么要使用生产者消费者模型

  生产者指的是生产数据的任务,消费者指的是处理数据的任务,在并发编程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者和消费者模式

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

这个阻塞队列就是用来给生产者和消费者解耦的

二 生产者消费者模型实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

from multiprocessing import Process, Queue

import time

import random

import os

def consumer(q, name):

    while True:

        res = q.get()

        time.sleep(random.randint(13))

        print("\033[43m %s 吃%s\033[0m" % (name, res))

def producer(q, name, food):

    for in range(3):

        time.sleep(random.randint(13))

        res = "%s%s" % (food, i)

        q.put(res)

        print("\033[45m %s 生产了 %s\033[0m" % (name, res))

if __name__ == '__main__':

    = Queue()

    # 生产者们:即厨师们

    p1 = Process(target=producer, args=(q, 'egon''包子'))

    # 消费者们:即吃货们

    c1 = Process(target=consumer, args=(q, 'mike'))

    # 开始

    p1.start()

    c1.start()

    print('主')

  执行结果

1

2

3

4

5

6

7

 egon 生产了 包子0

 egon 生产了 包子1

 mike 吃包子0

 egon 生产了 包子2

 mike 吃包子1

 mike 吃包子2

  

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

  解决方式无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

def consumer(q, name):

    while True:

        res = q.get()

        if res is None:

            break

        time.sleep(random.randint(13))

        print("\033[43m %s 吃%s\033[0m" % (name, res))

def producer(q, name, food):

    for in range(3):

        time.sleep(random.randint(13))

        res = "%s%s" % (food, i)

        q.put(res)

        print("\033[45m %s 生产了 %s\033[0m" % (name, res))

if __name__ == '__main__':

    = Queue()

    # 生产者们:即厨师们

    p1 = Process(target=producer, args=(q, 'egon''包子'))

    # 消费者们:即吃货们

    c1 = Process(target=consumer, args=(q, 'mike'))

    # 开始

    p1.start()

    c1.start()

    p1.join()

    q.put(None)

    print('主')

  但上述解决方法,在有多个生产者和多个消费者时,我们则需要用一个很low的方式去解决,有几个消费者就需要发送几次结束信号:相当low,例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

def consumer(q, name):

    while True:

        res = q.get()

        if res is None:

            break

        time.sleep(random.randint(13))

        print("\033[43m %s 吃%s\033[0m" % (name, res))

def producer(q, name, food):

    for in range(3):

        time.sleep(random.randint(13))

        res = "%s%s" % (food, i)

        q.put(res)

        print("\033[45m %s 生产了 %s\033[0m" % (name, res))

if __name__ == '__main__':

    = Queue()

    # 生产者们:即厨师们

    p1 = Process(target=producer, args=(q, 'egon1''包子'))

    p2 = Process(target=producer, args=(q, 'egon2''烧麦'))

    p3 = Process(target=producer, args=(q, 'egon3''豆浆'))

    # 消费者们:即吃货们

    c1 = Process(target=consumer, args=(q, 'mike1'))

    c2 = Process(target=consumer, args=(q, 'mike2'))

    # 开始

    p1.start()

    p2.start()

    p3.start()

    c1.start()

    c2.start()

    p1.join()

    p2.join()

    p3.join()

    q.put(None)

    q.put(None)

    q.put(None)

    print('主')

  其实,我们的思路无非是发送结束信号而已,有另外一种队列提供了这种机制

JoinableQueue(maxsize)

1

这就是一个Queue对象,但队列允许项目的使用者通知生产者项目已经被成功处理,通知进程时使用共享的信号和条件变量来实现的

  参数实现

1

maxsize是队列中允许最大项数,省略则无大小限制

  方法介绍

1

2

3

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

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

from multiprocessing import Process, JoinableQueue

import time

import random

def consumer(q, name):

    while True:

        res = q.get()

        time.sleep(random.randint(13))

        print('%s 吃 %s' % (name, res))

        q.task_done()  # 发送信号给q.join(),说明已经从队列中取走一个数据并处理完毕

def producer(q, name, food):

    for in range(3):

        time.sleep(random.randint(13))

        res = '%s%s' % (food, i)

        q.put(res)

        print('%s 生产了 %s' % (name, res))

    q.join()    # 等到消费者把自己放入队列中的所有的数据都取走之后,生产者才结束

if __name__ == '__main__':

    = JoinableQueue()     # 使用JoinableQueue()

    # 生产者:即厨师们

    p1 = Process(target=producer, args=(q, 'egon1''包子'))

    p2 = Process(target=producer, args=(q, 'egon2''烧麦'))

    p3 = Process(target=producer, args=(q, 'egon3''豆浆'))

    # 消费者们:即吃货们

    c1 = Process(target=consumer, args=(q, 'mike1'))

    c2 = Process(target=consumer, args=(q, 'mike2'))

    c1.daemon = True

    c2.daemon = True

    # 开始

    p1.start()

    p2.start()

    p3.start()

    c1.start()

    c2.start()

    p1.join()

    p2.join()

    p3.join()

    # 1、主进程等生产者p1,p2,p3结束

    # 2、而p1,p2,p3,是在消费者把所有数据都取干净之后才会结束

    # 3、所以一旦p1,p2,p3结束了,证明消费者也没必要存在了,应该随着主进程一块死掉,因而需要将生产者们设置成守护进程

    print("主")

  

三 生产者消费者模型总结

1、程序中有两类角色

1

2

一类负责生产数据(生产者)

一类负责处理数据(消费者)

2、引入生产者消费者模型为了解决的问题是

1

2

平衡生产者与消费者之间的速度差

程序解开耦合

3、如何实现生产者消费者模型

1

生产者<----->队列<------>消费者

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者消费者问题是多进程并发编程中的一个经典问题,解决该问题的方法之一是使用信号量。Python中的multiprocessing模块提供了Semaphore类来实现信号量。 在生产者消费者问题中,有两种角色:生产者消费者生产者负责生产数据,消费者负责消费数据。当生产者生产数据时,需要保证消费者不会消费同一个数据,当消费者消费数据时,需要保证生产者不会生产同一个数据。这就需要使用信号量来进行同步控制。 以下是一个使用信号量处理生产者消费者问题的Python程序示例: ```python import multiprocessing import time import random class Producer(multiprocessing.Process): def __init__(self, queue, semaphore): multiprocessing.Process.__init__(self) self.queue = queue self.semaphore = semaphore def run(self): for i in range(5): self.semaphore.acquire() item = random.randint(0, 100) print("Producer produced item: ", item) self.queue.put(item) time.sleep(1) class Consumer(multiprocessing.Process): def __init__(self, queue, semaphore): multiprocessing.Process.__init__(self) self.queue = queue self.semaphore = semaphore def run(self): while True: self.semaphore.acquire() if not self.queue.empty(): item = self.queue.get() print("Consumer consumed item: ", item) self.semaphore.release() if __name__ == '__main__': queue = multiprocessing.Queue() semaphore = multiprocessing.Semaphore(1) producer = Producer(queue, semaphore) consumer = Consumer(queue, semaphore) producer.start() consumer.start() producer.join() consumer.join() ``` 在该示例中,Producer类和Consumer类都继承了multiprocessing.Process类,用于创建子进程。Producer类负责生产数据,Consumer类负责消费数据。在run()方法中,使用semaphore.acquire()方法获取信号量,表示该进程需要占用资源。当生产者生产数据时,先获取信号量,然后将数据放入队列中。当消费者消费数据时,先获取信号量,然后判断队列是否为空,如果不为空,则从队列中取出数据进行消费。最后,都使用semaphore.release()方法释放信号量,表示该进程不再占用资源。 在主函数中,首先创建了一个队列和一个信号量,然后创建了一个生产者进程和一个消费者进程,分别启动这两个进程并等待它们执行完毕。运行该程序,可以看到生产者生产数据,消费者消费数据,并且保证不会出现重复的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值