进程之间的通信——管道/共享
导引
from multiprocessing import Pipe conn1,conn2=Pipe() conn1.send('hello') print(conn2.recv()) D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py hello Process finished with exit code 0
如上代码所示,conn1和conn2就好比是在一个管道的两端进行通信。
管道是一个双向通信的协议,且这两个连接再两个不同的进程中依然可以通信。
from multiprocessing import Pipe,Process def func(conn): conn.send('hello') if __name__=='__main__': conn1, conn2 = Pipe() p=Process(target=func,args=(conn1,)) p.start() print(conn2.recv())
上面的代码实现了在主进程和子进程之间的通信
进行修正,消除while循环执行不完全的现象
from multiprocessing import Pipe,Process def func(conn): while 1: msg = conn.recv() if msg is None: break print(msg) if __name__=='__main__': conn1, conn2 = Pipe() p=Process(target=func,args=(conn2,)) p.start() for i in range(10): conn1.send('hello') conn1.send(None)
另外一种方法:这是一个新的知识点
from multiprocessing import Pipe,Process def func(conn1,conn2): conn1.close() while 1: msg = conn2.recv() print(msg) if __name__=='__main__': conn1, conn2 = Pipe() p=Process(target=func,args=(conn1,conn2)) p.start() conn2.close() for i in range(10): conn1.send('hello') conn1.close()
由于conn1只发送了10条数据,所以当10条数据全部发送完的时候,conn2.recv()还在继续执行就会报错,就会弹出EOFError错误。
from multiprocessing import Pipe,Process def func(conn1,conn2): conn1.close() while 1: try: msg = conn2.recv() print(msg) except: EOFError#既然会出现这个错误,那么我们就把这个错误排除 conn2.close() break if __name__=='__main__': conn1, conn2 = Pipe() p=Process(target=func,args=(conn1,conn2)) p.start() conn2.close() for i in range(10): conn1.send('hello') conn1.close()
利用管道来实现生产者和消费者模型
from multiprocessing import Pipe,Process import time import random def producer(con,pro,name,food): con.close() for i in range(10): f='%s生产了%s%s'%(name,food,i) time.sleep(random.randint(1, 2)) print(f) pro.send(f) pro.close() def consumer(con,pro,name): pro.close() while 1: try: food=con.recv() print('\33[31m%s吃了%s\33[0m'%(name,food)) time.sleep(random.randint(1,2)) except: EOFError con.close() break if __name__=='__main__': con,pro = Pipe() p=Process(target=producer,args=(con,pro,'tom','包子')) p.start() c=Process(target=consumer,args=(con,pro,'JACK')) c.start() con.close() pro.close()
但是管道有一个缺点就是,不安全。可以这样考虑,由于管道一端传向另一端的时候,可能存在多个接收者,那么就有可能造成给甲的数据,被乙接收的现象,造成数据的不安全。此时可以考虑加上一把锁,注意下面代码中加锁的位置。
from multiprocessing import Pipe,Process,Lock import time import random def producer(con,pro,name,food): con.close() for i in range(10): f='%s生产了%s%s'%(name,food,i) # time.sleep(random.randint(1, 2)) print(f) pro.send(f) pro.send(None) pro.send(None) pro.close() def consumer(con,pro,name,lock): pro.close() while 1: lock.acquire() food=con.recv() lock.release() if food is None: break con.close() print('\33[31m%s吃了%s\33[0m'%(name,food)) if __name__=='__main__': lock = Lock() con,pro = Pipe() p=Process(target=producer,args=(con,pro,'tom','包子')) p.start() c1=Process(target=consumer,args=(con,pro,'JACK',lock)) c1.start() c2 = Process(target=consumer, args=(con, pro, 'Json', lock)) c2.start() con.close() pro.close()
还可以这样用下面的代码
from multiprocessing import Pipe,Process,Lock import time import random def producer(con,pro,name,food): con.close() for i in range(10): f='%s生产了%s%s'%(name,food,i) time.sleep(random.randint(1, 2)) print(f) pro.send(f) pro.send(None) pro.send(None) pro.close() def consumer(con,pro,name,lock): pro.close() while 1: lock.acquire() food=con.recv() lock.release() if food: print('\33[31m%s吃了%s\33[0m'%(name,food)) else: con.close() break if __name__=='__main__': lock = Lock() con,pro = Pipe() p=Process(target=producer,args=(con,pro,'tom','包子')) p.start() c1=Process(target=consumer,args=(con,pro,'JACK',lock)) c1.start() c2 = Process(target=consumer, args=(con, pro, 'Json', lock)) c2.start() con.close() pro.close()
队列实际上就是管道+锁,所以未来多用管道。
共享——Manager
from multiprocessing import Process,Manager def func(dic): dic['count']-=1 print(dic) if __name__=='__main__': m=Manager() dic=m.dict({'count':100}) p_list=[] p=Process(target=func,args=(dic,)) p.start() p.join() print('主进程:',dic)#和子进程中的dic完全一致,都是子进程修改后的dic
修改
from multiprocessing import Process,Manager def func(dic): dic['count']-=1 # print(dic) if __name__=='__main__': m=Manager() dic=m.dict({'count':100}) p_list=[] for i in range(50): p=Process(target=func,args=(dic,)) p.start() p_list.append(p) for i in p_list:#这里得到的p_list是一个包含有50个进程的列表,列表中的每个元素都是一个进程 i.join() print('主进程:',dic)
按照老师的代码这里出现了每次运行结果不一致的情况,但是自己运行多次没有出现这种状况。
按照老师的意思,由于每次运行结果不一致需要加锁
from multiprocessing import Process,Manager,Lock def func(dic,lock): lock.acquire() dic['count']-=1 lock.release() # print(dic) if __name__=='__main__': m=Manager() lock=Lock() dic=m.dict({'count':100}) p_list=[] for i in range(50): p=Process(target=func,args=(dic,lock)) p.start() p_list.append(p) for i in p_list:#这里得到的p_list是一个包含有50个进程的列表,列表中的每个元素都是一个进程 i.join() print('主进程:',dic)
总结:
进程的同步控制——锁、信号量、事件都是对进程的控制;
队列和管道是进程的通信;
Manager是进程之间的数据共享