Pipe 管道(了解)

#创建管道的类: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异常。

管道

支持双向通信

在进程之间通信的工具

管道 + 锁 = 队列

管道--数据不安全

示例:

from multiprocessing import Pipe
left, right = Pipe()  # 左,右 即管道的两端
left.send('1234')  # 左边
sendprint(right.recv())  # 右边接收

执行结果

1234

注释:

管道实例化之后,形成2端

左边send,右边接收

一端收发,会阻塞

示例:

from multiprocessing import Process,Pipe

def f(parent_conn, child_conn):
    parent_conn.close()  # 不写close将会引发EOFError错误   
    while True:
        try:
            print(child_conn.recv())
        except EOFError:
            child_conn.close()
            breakif __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=f,args=(parent_conn,child_conn))
    p.start()
    child_conn.close()
    parent_conn.send('hello')
    parent_conn.send('hi')
    parent_conn.send('你好')
    parent_conn.close()
    p.join()

执行结果

hello

hi

你好

pipe实现生产者消费者模型

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:
            breakdef producer(seq, p):
    produce, consume = p
    consume.close()
    for i in seq:
        produce.send(i)
        
if __name__ == '__main__':
    produce, consume = Pipe()
    c1 = Process(target=consumer, args=((produce, consume), 'c1'))
    c1.start()
    seq = (i for i in range(5))
    producer(seq, (produce, consume))
    produce.close()
    consume.close()
    c1.join()
    print('主进程')

执行结果

c1 收到包子:0

c1 收到包子:1

c1 收到包子:2

c1 收到包子:3

c1 收到包子:4

主进程

多个消费之之间的竞争问题带来的数据不安全问题

from multiprocessing import Process,Pipe,Lock

def consumer(p,name,lock):
    produce, consume=p
    produce.close()
    while True:
        lock.acquire()
        baozi=consume.recv()
        lock.release()
        if baozi:
            print('%s 收到包子:%s' %(name,baozi))
        else:
            consume.close()
            breakdef producer(p,n):
    produce, consume=p
    consume.close()
    for i in range(n):
        produce.send(i)
    produce.send(None)
    produce.send(None)
    produce.close()
if __name__ == '__main__':
    produce,consume=Pipe()
    lock = Lock()
    c1=Process(target=consumer,args=((produce,consume),'c1',lock))
    c2=Process(target=consumer,args=((produce,consume),'c2',lock))
    p1=Process(target=producer,args=((produce,consume),6))
    c1.start()
    c2.start()
    p1.start()

    produce.close()
    consume.close()

    c1.join()
    c2.join()
    p1.join()
    print('主进程')

执行结果

c2 收到包子:1

c2 收到包子:2

c2 收到包子:3

c2 收到包子:4

c2 收到包子:5

主进程

进程之间的数据共享

展望未来,基于消息传递的并发编程是大势所趋

即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合,通过消息队列交换数据。

这样极大地减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中。

但进程间应该尽量避免通信,即便需要通信,也应该选择进程安全的工具来避免加锁带来的问题。

以后我们会尝试使用数据库来解决现在进程之间的数据共享问题