进程之间的通信——管道/共享

导引

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是进程之间的数据共享

 

posted @ 2019-04-11 23:57  舒畅123  阅读(288)  评论(0编辑  收藏  举报