多进程知识2

多进程2

主要内容:

  • 进程间通信机制(IPC)
  • 生产者消费者模型
  • JoinableQueue

 

1. 进程间通信机制(IPC)

  在内存中每个进程都独立开辟了一块内存空间, 它们都是互不干扰影响的, 每个进程都不能访问其他进程的变量等等. 而这就给进程间通信带来了挑战, 在Python中, 我们需要借助管道来进行IPC通信, 它的具体体现就是运用threading模块的队列Queue来完成.

 

2. 生产者消费者模型

 

  所谓生产者消费者模型指的是一种设计模式. 生产者是生产数据的一方, 消费者是消费数据的一方,  现在我们假设没有把这两个角色分开, 而是把它们放在一块, 那么消费者每次需要获取数据就只能够等待生产者生产出数据才进行下一步, 或者生产者只能等待消费者消耗完数据才能进行下一步行动, 这样他们之间的联系就会非常的紧密, 当两者的速度完全不匹配, 也就是供需失衡的时候, 程序的处理数据效率就会大大降低,  现在我们把它们单独抽取出来, 消费者只需要管生产数据的事, 并把生产完的数据放到一个队列中, 而消费者则只需要从管道中获取数据, 这样生产者与消费者两者就可以借助这个起缓冲作用的阻塞队列实现完全解耦了.

 

接下来是一个利用队列进行进行进程间通信的生产者消费者模型的小程序

IPC

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


def producer(q):
    for i in range(5):
        q.put(i)
        print('生产中...')
        time.sleep(random())

    print('{}生产完毕---'.format(current_process().name))


def consumer(q):
    while True:
        data = q.get()
        time.sleep(random())
        print('{}获取到了{}'.format(current_process().name, data))


if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer, args=(q,))
    c = Process(target=consumer, args=(q,))

    p.start()
    c.start()
    print('主进程...')

 

主进程...
生产中...
生产中...
Process-2获取到了0
Process-2获取到了1
生产中...
Process-2获取到了2
生产中...
生产中...
Process-1生产完毕---
Process-2获取到了3
Process-2获取到了4
运行结果

  使用了管道后, 进程间就能够相互之间借助管道来通信了,  但上面这个代码还有个缺点, 就是consumer这个进程不知道什么时候可以结束循环, 会一直在get方法那边阻塞, 现在就需要让程序能够正常运行结束, 也就是让所有进程知道生产者进程什么时候能够生产完毕. 下面就是上面这个程序的改进.

 

IPC通信改进

  现在的任务是需要知道生产者什么时候能够生产完毕, 那么我们就可以在主进程等待生产者进程运行结束后, 向阻塞队列里发送一个None信号, 这样消费者进程在接收到None信号, 就知道这时候已经生产结束了, 就不用再继续等待获取数据了.

  下面是改进后的程序.

 

# -*- coding: UTF-8 -*-

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


def producer(q):
    for i in range(5):
        time.sleep(random.random())
        q.put(i)
        print('{}生产了{}'.format(current_process().name, i))


def consumer(q):
    while True:
        data = q.get()
        if data is None:
            break
        time.sleep(random.random())
        print('{}消费者了{}'.format(current_process().name, data))


if __name__ == '__main__':
    q = Queue()

    p1 = Process(target=producer, args=(q, ))
    p2 = Process(target=producer, args=(q, ))
    c1 = Process(target=consumer, args=(q, ))
    c2 = Process(target=consumer, args=(q, ))

    p1.start()
    p2.start()
    c1.start()
    c2.start()

    p1.join()
    p2.join()
    print('生产完毕')
    q.put(None)
    q.put(None)

 

Process-2生产了0
Process-1生产了0
Process-1生产了1
Process-3消费者了0
Process-2生产了1
Process-1生产了2
Process-3消费者了1
Process-4消费者了0
Process-1生产了3
Process-2生产了2
Process-1生产了4
Process-3消费者了1
Process-3消费者了3
Process-4消费者了2
Process-4消费者了4
Process-2生产了3
Process-3消费者了2
Process-2生产了4
Process-4消费者了3
生产完毕
Process-3消费者了4
运行结果

3. JoinableQueue

  上面的解决方式其实也非常的不优雅, 我们有多少个消费者就需要往队列里发送多少次None信号, 这种解决问题的方式总觉得有点怪, 所以Python中也提供了这么一个可以等待的队列, 来专门解决这个问题.

  它的使用方法与普通队列并没有什么不同. 只是多了两个方法.

  task_done:  这是当消费者把数据获取到并处理完毕,  可以向队列里发送这个处理完毕的信号.

  join:  这个方法是需要当队列里所有数据都处理完毕才会结束阻塞状态. 

 

# -*- coding: UTF-8 -*-


from multiprocessing import JoinableQueue, Process, current_process

import time
import random


def producer(q):
    for i in range(5):
        time.sleep(random.random())
        q.put(i)
        print('{}生产了{}'.format(current_process().name, i))


def consumer(q):
    while True:
        data = q.get()
        time.sleep(random.random())
        print('{}消费了{}'.format(current_process().name, data))
        q.task_done()


if __name__ == '__main__':
    q = JoinableQueue()
    p1 = Process(target=producer, args=(q, ))
    p2 = Process(target=producer, args=(q, ))
    c1 = Process(target=consumer, args=(q, ))
    c2 = Process(target=consumer, args=(q, ))

    # 这里需要设置消费者为守护进程, 当主进程运行结束, 也就说明
    # 生产者把数据都消耗完毕了.
    c1.daemon = True
    c2.daemon = True
    p1.start()
    p2.start()
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    q.join()
posted @ 2019-08-12 16:15  yscl  阅读(97)  评论(0)    收藏  举报