进程间通信
IPC
队列
1,正常队列
from queue import Queue
先进先出FIFO ----维护秩序时用的比较多
q = Queue() ##q是一个队列
print(q)
q.put(12) ##q.put 是往q里面放东西
print(q.get()) ## q.get() 是拿q里面的东西
print(q.qsize()) ##队列的长度
栈:先进后出 ----算法用的比较多
用栈实现三级菜单
计算文件夹的总大小
print(q.get()) ##没有值得时候回阻塞
print(q.get_nowait()) ##当有值的时候取值
print(q.get_nowait()) #没有值的时候会报错
进程中的队列
from multiprocessing import Queue
创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递
q.empty()
判断队列是否为空,返回布尔值
q.full()
判断队列是否满了,返回布尔值
主进程放,子进程取
from multiprocessing import Queue,Process
def con(q):
print(q.get())
if __name__ == '__main__':
q = Queue()
p = Process(target=con,args=(q,))
p.start()
q.put(123)
得到打印结果 : 123
子进程放,另一个子进程取
from multiprocessing import Queue,Process
def con(q):
print(q.get())
def pro(q):
q.put(123)
if __name__ =='__main__':
q = Queue()
p = Process(target=con,args=(q,))
p.start()
p= Process(target=pro,args=(q,))
p.start()
生产者消费者模型
生产者消费者模型 --解决创造(生产)数据和处理(消费)数据的效率不平衡问题
把创造数据 和 处理数据放在不同的进程中,
根据他们的效率在调整进程的个数
生产数据快,消费数据慢,内存空间的浪费
消费数据快,生产数据慢,效率低下
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()
p2.join()
q.put('stop')
q.put('stop')
让consumer停下来的方法
在所有生产者结束生产之后,向队列中放入一个结束符
有几个consumer就向队列中放几个结束符
在消费者消费的过程中,接收到结束符,就结束消费的进程
Joinable Queue
创建可连接的共享进程队列,这就像是一个Queue对象,但队列润许项目的使用者通知生产者项目已被
成功处理.通知进程是使用共享信号和条件变量来实现的
import time
import random
from multiprocessing import JoinableQueue,Process
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()
只有multiprocessing 中的队列 才能帮助你 实现 IPC
永远不可能出现数据不安全的情况,多个进程不会同时取走同一个数据
提供给的方法
put
get
put_nowwait
get_nowwait
empty --- 在多进程内不可靠
full --- 在多进程内不可能
qsize --- 在多进程内不可靠
有图先进先出的特点 + 进程通信的功能 +数据进程安全,经常用它来完成进程之间的通信
生产者消费者模型
生产者和消费者的效率平衡的问题
内存的控制 - 队列的长度限制
让消费者自动停下来
joinableQueue
在消费数据的时候 task_done
在生产端\主进程 join
管道
队列就是基于管道实现的
队列 数据安全的
管道 数据不安全的
队列 = 管道 + 锁
#管道初使用
from multiprocessing import Pipe
left,right = Pipe()
left.send('aaa')
print(right.recv())
#创建管道的类 pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道 #参数介绍: dumplex:默认管道是全双工的,如果将duplex射程False,conn1只能用于接收,conn2只能用于发送 #主要方法: conn1.recv():接收conn2.send(obj) 发送的对象.如果没有消息可接收,recv方法会一直阻塞.如果连接的另外一端已经关闭,那么recv方法会跑出EOFError. conn1.send(obj);通过连接发送对象.obj是与序列化兼容的任意对象 #其他方法: conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法 conn1.fileno():返回连接使用的整数文件描述符 conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。 conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。 conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收 conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。
##EOFError## #在这一个进程中 如果不在用这个断电了,应该close #这样在recv的时候,如果其他断电都被关闭了,就能够知道不会再有新的消息传进来 #此时就不会在这里阻塞等待,而是跑出一个EOFError # close 并不是关闭了整个管道,而是修改了操作系统对管道端点的引用计数的处理 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()
管道是由操作系统进行引用计数的,必须在所有进程中关闭管道后才能生成EOFError异常
from multiprocessing import Process,Pipe def consumer(p,name): produce, consume=p produce.close() while True: try: baozi=consume.recv() print('%s 收到包子:%s' %(name,baozi)) except EOFError: break def producer(p,seq=10): produce, consume=p consume.close() for i in range(seq): produce.send(i) if __name__ == '__main__': produce,consume=Pipe() for i in range(5): c=Process(target=consumer,args=((produce,consume),'c1')) c.start() for i in range(5): p = Process(target=producer, args=((produce, consume))) p.start() producer((produce,consume)) produce.close() consume.close()
进程之间的数据共享
子进程可以用住进程里面的东西,想用就用,不想用就不用
from multiprocessing import Manager,Process,Lock def work(d,lock): with lock: #不枷锁的话 容易造成数据错乱 d['count']-=1 if __name__ == '__main__': lock = Lock() m = Manager() 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)
进程池
进程池的概念:
定义一个池子,在里面放上固定数量的进程,有需求来了,就拿池中的进程来处理任务,等到处理完毕,进程并不关闭
,而是将进程再放会进程池中继续等待任务.如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程
执行完毕,才能继续执行,就是说,池中的进程数量是固定的,那么同一时间最多有固定数量的进程在执行,这样不会增加
系统的调度难度,还节省开闭进程的时间,也一定程度上能够实现并发效果.
import os import time from multiprocessing import Pool def func(i): time.sleep(0.4) print(os.getpid(),i) if __name__ =='__main__': p = Pool(5) #有5个进程数量 for i in range(20): #20个等待执行 p.apply_async(func,args= (i,)) p.close() p.join()