进程间通讯
windows下进程间通讯有很多种,例如:消息队列、共享内存、管道等等。
Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

Pipes

multiprocessing.Pipe()即管道模式,调用Pipe()返回管道的两端的Connection。

Python官方文档的描述:
Returns a pair (conn1, conn2) of Connection objects representing the ends of a pipe.

因此, Pipe仅仅适用于只有两个进程一读一写的单双工情况,也就是说信息是只向一个方向流动。

Pipe的读写效率要高于Queue。

进程间的Pipe基于fork机制建立。

当主进程创建Pipe的时候,Pipe的两个Connections连接的的都是主进程。

当主进程创建子进程后,Connections也被拷贝了一份。此时有了4个Connections。

此后,关闭主进程的一个Out Connection,关闭一个子进程的一个In Connection。那么就建立好了一个输入在主进程,输出在子进程的管道。

原理示意图如下:

进程间 消息队列 python python 进程间通讯_进程间通信

下面看一个例子:

# coding=utf-8
import os
from multiprocessing import Pipe, Process

def sub_process(parentid, pipe):
    print('子进程运行 %s   父进程 %s' % (os.getpid(), parentid))
    out_pipe, in_pipe = pipe

    in_pipe.close()
    while True:
        try:
            msg = out_pipe.recv()
            print('out pipe : %s' % msg)
            # out_pipe.send(msg*10)
        except EOFError:
            break



if __name__ == '__main__':
    print('主进程运行 %s' % os.getpid())
    out_pipt, in_pipe = Pipe(True)
    subprocess = Process(target=sub_process, args=(os.getpid(), (out_pipt, in_pipe)))
    subprocess.start()

    # 关闭主进程的输出端
    out_pipt.close()
    for i in range(10):
        in_pipe.send(i)
        # print('in  pipe : %s' % in_pipe.recv())
    in_pipe.close()

    subprocess.join()
    print('主进程结束 %s' % os.getpid())

#打印结果
主进程运行 4288
子进程运行 4964   父进程 4288
out pipe : 0
out pipe : 1
out pipe : 2
out pipe : 3
out pipe : 4
out pipe : 5
out pipe : 6
out pipe : 7
out pipe : 8
out pipe : 9
主进程结束 4288

当然不关闭输入输出管道也可以,但是需要自己做同步。

总结一下:
上面的代码中主要用到了pipe的send()、recv()、close()方法。当pipe的输入端被关闭,且无法接收到输入的值,那么就会抛出EOFError。
新建一个Pipe(duplex)的时候,如果duplex为True,那么创建的管道是双向的;如果duplex为False,那么创建的管道是单向的。

Queue

Queue据官方文档也是基于pipe的实现。
Queue的使用主要是一边put(),一边get().但是Queue可以是多个Process 进行put操作,也可以是多个Process进行get()操作。

# coding=utf-8
import os
from multiprocessing import Queue, Process
import random

def getter(name, queue):
    print('subprocess %s : %s' % (name, os.getpid()))
    while True:
        try:
            value = queue.get(True, 10)
            # block为True,就是如果队列中无数据了。
            #   |—————— 若timeout默认是None,那么会一直等待下去。
            #   |—————— 若timeout设置了时间,那么会等待timeout秒后才会抛出Queue.Empty异常
            # block 为False,如果队列中无数据,就抛出Queue.Empty异常
            print('subprocess %s : get value %f' % (name,value))
        except Exception:
            break


def putter(name, queue):
    print('subprocess %s : %s' % (name, os.getpid()))
    for i in range(0,5):
        value = random.random()
        queue.put(value)
        # 放入数据 put(obj[, block[, timeout]])
        # 若block为True,如队列是满的:
        #  |—————— 若timeout是默认None,那么就会一直等下去
        #  |—————— 若timeout设置了等待时间,那么会等待timeout秒后,如果还是满的,那么就抛出Queue.Full.
        # 若block是False,如果队列满了,直接抛出Queue.Full
        print('subprocess %s : put value %f' % (name,value))


if __name__ == '__main__':
    print('parent process run %s ' % os.getpid())
    queue = Queue(2)#队列大小为2
    getter_process = Process(target=getter, args=('getter', queue))
    putter_process = Process(target=putter, args=('putter', queue))

    getter_process.start()
    putter_process.start()

#打印结果
parent process run 8852 
subprocess getter : 5480
subprocess putter : 8732
subprocess putter : put value 0.953115
subprocess putter : put value 0.281539
subprocess getter : get value 0.953115
subprocess putter : put value 0.459467
subprocess getter : get value 0.281539
subprocess putter : put value 0.639799
subprocess getter : get value 0.459467
subprocess putter : put value 0.114373
subprocess getter : get value 0.639799
subprocess getter : get value 0.114373