进程间通信 --- 队列, 管道. Manager模块 进程池: Pool

一, 进程间通信 --- 队列, 管道.

  ps: IPC == 进程之间的通信

  1, 队列:

    1, 正常队列: from queue import Queue
      queue.Queue 学名(FIFO) 先进先出 -- 维护秩序的时候用的比较多,
      q = Queue()        实例化队列对象, 可以传一个最大元素个数  
      put()            放入 如果超出该队列最大元素个数 则 堵塞
      put_nowait()      放入, 如果超出,会报错.
      get()           拿出 等同于list.pop, 如果没有值 会一直堵塞.
      get_nowait()            拿出, 只不过当没有值的时候 会报错.
      qsize()             查询队列中元素的个数.
      full()           判断队列是否满了 返回布尔值
      empty()         判断段队列是否为空 返回布尔值

    ps: 栈 先进后出 -- 常用于算法.

    2, 进程中的队列: from multiprocessing import Queue

      方法同上, 唯一不同的是 full() empty()在进程中的值不够准确

from multiprocessing import Queue, Process
import time

def get_q(q):
    print(q.get())

def put_q(q):
    time.sleep(2)
    q.put(123)

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=get_q,args=(q,)).start()
    p2 = Process(target=put_q,args=(q,)).start()

简单的进程中队列的代码

  

3, 生产者消费者模型:解决创造(生产)数据 和处理(消费)数据的效率不平衡的问题

      把创造数据 和 处理 数据放在不同的进程中, 根据他们的效率来调整进程的个数

      1, 生产数据快, 消费数据慢, 内存空间的浪费
      2, 消费数据快, 生产数据慢, 效率低下.
      ps: consumer 消费者 producer 生产者.

# 此代码会导致 程序一直处于get() 的堵塞中.
# 意味着 消费者没有的进程还没结束.
# 不要着急, 后面会有解决此问题的方法

import time
import random
from multiprocessing import Process,Queue

def consumer(q,name):
    while True:
        food = q.get()
        if food == 'stop':break
        print('%s 吃了 %s'%(name,food))
        time.sleep(random.random())

def producer(q,name,food,n=10):
    for i in range(n):
        time.sleep(random.random())
        fd = food+str(i)
        print('%s 生产了 %s'%(name,fd))
        q.put(fd)

if __name__ == '__main__':
    q = Queue(10)
    c1 = Process(target=consumer,args=(q,'alex'))
    c1.start()
    c2 = Process(target=consumer, args=(q, 'alex'))
    c2.start()
    p1 = Process(target=producer,args=(q,'太白','泔水'))
    p1.start()
    p2 = Process(target=producer, args=(q, 'egon', '鱼刺'))
    p2.start()

基于Queue实现的模型

  3, 让消费者自动停下来:
        1, 在所有的生产者生产结束后, 向队列中放一个结束符
        2, 有几个consumer(消费者) 就向队列中放几个结束符
        3, 在消费者消费的过程中, 接收到结束符, 就结束消费进程

import time
import random
from multiprocessing import Process,Queue

def consumer(q,name):
    while True:
        food = q.get()
        if food == 'stop':break
        print('%s 吃了 %s'%(name,food))
        time.sleep(random.random())

def producer(q,name,food,n=10):
    for i in range(n):
        time.sleep(random.random())
        fd = food+str(i)
        print('%s 生产了 %s'%(name,fd))
        q.put(fd)

if __name__ == '__main__':
    q = Queue(10)
    c1 = Process(target=consumer,args=(q,'alex'))
    c1.start()
    c2 = Process(target=consumer, args=(q, 'alex'))
    c2.start()
    p1 = Process(target=producer,args=(q,'太白','泔水'))
    p1.start()
    p2 = Process(target=producer, args=(q, 'egon', '鱼刺'))
    p2.start()
    p1.join()     #     让主进程 join住,去等待第一个生产者生产完毕
    p2.join()     #     让主进程 join住,去等待第二个生产者生产完毕
    q.put('stop')    #    然后在 queue中 put一个停止符, 有几个消费者就 put几个停止符
    q.put('stop')

解决消费者一直处于堵塞状态的问题

  

 4, 此方法 看起来比较low 不够自动化, 还得让 程序员先去确认有几个 消费者, 要知道消费者 本身就是一个不确定因素, 所以大神们 写出来一个模块叫做 JoinableQueue

      4, JoinableQueue 比Queue 多了两个方法,并且内部多了一个计数器.:  此模块用来解决消费者被堵塞的问题

        ps: 这里的消费者要作为主进程的一个守护进程存在.
        1, join() 堵塞等消费者消费完毕之后结束堵塞状态
        2, task_done() 让队列中的计数器-1

import time
import random
from multiprocessing import JoinableQueue,Process
# join  阻塞
def consumer(q,name):
    while True:
        food = q.get()
        print('%s 吃了 %s'%(name,food))
        time.sleep(random.random())
        q.task_done()

def producer(q,name,food,n=10):
    for i in range(n):
        time.sleep(random.random())
        fd = food+str(i)
        print('%s 生产了 %s'%(name,fd))
        q.put(fd)
    # q.join()

if __name__ == '__main__':
    q = JoinableQueue()
    c1 = Process(target=consumer,args=(q,'alex'))
    c1.daemon = True
    c1.start()
    c2 = Process(target=consumer, args=(q, 'alex'))
    c2.daemon = True
    c2.start()
    p1 = Process(target=producer,args=(q,'太白','泔水'))
    p1.start()
    p2 = Process(target=producer, args=(q, 'egon', '鱼刺'))
    p2.start()
    p1.join()
    p2.join()  # 此进程的join 必须存在.
    q.join()  # 此方法可放在 函数中, 也可放在主进程中

JoinableQueue解决消费者停下的问题

JoinableQueue解决消费者停下的问题

  

ps: 1, 只有在 multiprocessing 中的队列, 才能帮助你实现PIC
        2, 永远不可能出现 数据不安全的情况, 多个进程不会同时get同一个数据
        3, 由于先进先出的特点+ 进程通信的功能+ 数据进程安全, 经常用它来完成进程之间的通信

  2, 管道: 队列就是基于管道实现的.
    队列 数据是安全的.
    管道 数据不安全的
    队列 = 管道 + 锁

    方法:
      from multiprocessing import Pipe
      1, left,right = Pipe() 此实例化会产生两个端点对象
      2, (left/right).recv() 接受另外一端发来的消息
      3, (left/right).send() 向另外一端发送消息

        ps: 因为 管道是在文件中进行传递消息, 所以 不用要求 必须是bytes类型的数据, 所以 reve/send 两个方法没有太多的要求.
      4, (left/right).close() 关闭某一端
    注意: 在不同的进程中, 每用 left/right 都会在管道中开一个接口,所以,必须要把没用的接口全部关闭

from multiprocessing import Pipe
left,right = Pipe()
left.send('aaa')
print(right.recv())

from multiprocessing import Pipe,Process
def consumer(left,right):
    left.close()
    while True:
        print(right.recv())
 
if __name__ == '__main__':
    left,right = Pipe()
    p = Process(target=consumer,args=(left,right))
    p.start()
    for i in range(10):
        left.send('hello')


'''
程序会一直停在 right.reve()
'''

没有关闭端点的代码

  EOF异常的触发:
      1, 在一个进程中,如果不在用 left/right 端口则应该用close()关闭该端口
      2, 如果其他端点都被关闭, 只剩下一个端点在 reve()的时候,该端点就能够知道不会再有新的消息传来
      3, 此时 recv() 不会在进行堵塞, 则抛出 EOFError异常
      4, 注意: close() 并没有将整个管道关闭, 实际,是给操作系统对管道端点的计数处理的功能执行-1的操作

from multiprocessing import Pipe
left,right = Pipe()
left.send('aaa')
print(right.recv())

from multiprocessing import Pipe,Process
def consumer(left,right):
    left.close()
    while True:
        #try:
        print(right.recv())
        #except EOFError:
            #break

if __name__ == '__main__':
    left,right = Pipe()
    p = Process(target=consumer,args=(left,right))
    p.start()
    right.close()
    for i in range(10):
        left.send('hello')
    left.close()

'''
此时程序在执行完后得到你想要的结果后 会报错, 
在 right.reve() 前后加上 try 就可以了.
'''

关闭无用端点的代码

  二, 进程之间的数据共享: Manager模块
  此模块中包含一些平常常用的数据类型,可以将原本不可以在进程与进程之间共享的数据, 进行共享
  因为此共享是基于管道来实现, 所以, 此模块中的数据类型除了队列,是不安全的.

进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的
虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此

A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.

A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.

重要的内容说三遍:
'''
list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.

list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.

list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.
'''

Manager模块简介以及包含的数据类型

  

from multiprocessing import Manager,Process,Lock
def work(d,lock):
    with lock: #不加锁而操作共享的数据,肯定会出现数据错乱
        d['count']-=1

if __name__ == '__main__':
    lock=Lock()
    with Manager() as m:
        dic=m.dict({'count':100})
        p_l=[]
        for i in range(100):
            p=Process(target=work,args=(dic,lock))
            p_l.append(p)
            p.start()
        for p in p_l:
            p.join()
        print(dic)

简单实现 Manager模块

  

三, 进程池: Pool    (面向高计算型的场景, 采用多进程.)

 

  进程池出现的原因:

    进程不能不限制的开启, 会给os的调度增加负担,且 正真能被同时执行的进程最多也就和 CPU个数相等, 所以此时就出现了进程池的概念, 同时打开和CUP个数相等的进程, 每执行完一个再进来一个进程, 这样限制了同时打开的进程的个数, 有利于os系统的调度, 同时也能节省内存.

  apply(func) 将某个函数 执行 同步, 有返回值.

import os
import time
from multiprocessing import Pool
# 同步请求的
def wahaha():
    time.sleep(1)
    print(os.getpid())
    return True

if __name__ == '__main__':
    p = Pool(5)  # CPU的个数 或者 +1
    ret_l = []
    for i in range(20):
       ret = p.apply(func = wahaha)   # 同步的,不常用
       print(ret)

'''
会一个一个打印, getpid
达不到想通过进程来提高运行速度的目的.
所以一般不用这个同步方法: apply()
'''

同步进程池 简易例子

  apply_async() 带着'async'都是异步 返回一个队列对象.
    1, 如果是异步的提交任务, 那么任务提交之后进程池和主进程也异步了, 主进程不会自动等待进程池中的任务执行完毕
    2, 如果需要主进程等待, 则用 join(), 但是join()是依赖close()
    3, 如果函数有返回值,
      可以通过ret.get() 来获取返回值.
      但如果一边提交一遍获取返回值会让程序变成同步的
      所以想要保留异步的效果, 应该将返回对象保存在列表里, 所有任务提交完成之后,再来取结果
      这种方式也可以去掉join.
  close() 关闭进程池, 让任务不能继续提交.
  join() 堵塞等待子进程结束

import os
import time
from multiprocessing import Pool

# 异步提交,获取返回值,等待所有任务都执行完毕之后再统一获取结果
def wahaha():
    time.sleep(1)
    print(os.getpid())
    return True

if __name__ == '__main__':
    p = Pool(5)  # CPU的个数 或者 +1
    ret_l = []
    for i in range(20):
       ret = p.apply_async(func = wahaha) # async  异步的
       ret_l.append(ret)
    p.close()  # 关闭 进程池中的进程不工作了
               # 而是关闭了进程池,让任务不能再继续提交了
    p.join()   # 等待这个池中提交的任务都执行完
    for ret in ret_l:
        print(ret.get())

'''
此代码会等所有进程执行完后, 打印返回值.
进程执行会呈现为 每5个为一组 噗噗噗的出来
返回值, 是因为 pool能取到返回值
'''

异步进程池 简易例子

  回调函数: -- 在主进程中执行.
    在发起任务的时候, 指定callback参数, 在每个进程执行完任务之后, 返回值会直接作为参数传递给callback的函数, 然后执行 callback函数中的代码

import os
import time
import random
from multiprocessing import Pool

# 异步提交,获取返回值,从头到尾一个任务执行完毕之后就可以获取到一个结果
def wahaha(num):
    time.sleep(random.random())
    print('pid : ',os.getpid(),num)
    return num

def back(arg):
    print('call_back : ',os.getpid(),arg)

if __name__ == '__main__':
    print('主进程',os.getpid())
    p = Pool(5)  # CPU的个数 或者 +1
    for i in range(20):
       ret = p.apply_async(func = wahaha,args=(i,),callback=back) # async  异步的
    p.close()
    p.join()


# 回调函数 _ 在主进程中执行
# 在发起任务的时候 指定callback参数
# 在每个进程执行完apply_async任务之后,返回值会直接作为参数传递给callback的函数,执行callback函数中的代码

pool callback 回调函数

  

转载于:https://www.cnblogs.com/123zzy/p/9519029.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值