多个进程之间肯定是要能够互相通信的,Python为进程通信提供了许多机制,我们以multiprocessing模块下的Queue和Pipe为例。
Queue
Queue是多进程安全队列,使用Queue可以实现多进程之间的数据传递。
1.Queue的常用方法
Queue.get(block=True,timeout)
1)获取队列的一条消息,然后将该消息自队列中移除。参数block默认为True。
2)如果block为True,且未设置timeout,当在Queue为空时调用该方法,该方法将被阻塞,直到从Queue中读取到数据为止;如果设置了timeout,则会等待timeout秒,之后若仍为读到任何数据,则抛出"Queue Empty"异常。
3)如果block为False,调用该方法时Queue为空,则立即抛出"Queue Empty"异常。
Queue.get_nowait()
相当于Queue.get(False)。
Queue.put(item,block=True,timeout)
1)将消息item写入Queue,block默认为True。
2)如果block为True,且未设置timeout,当在调用该方法时发现Queue已无空间可写入,此时该方法将被堵塞,直到Queue能够写入为止;如果设置了timeout,则会等待timeout秒,若之后仍然无法写入,抛出"Queue.Full"异常。
3)如果block为False,且调用该方法时Queue已无空间可写入,立刻抛出"Queue.Full"异常。
Queue.put_nowait(item)
相当于Queue.put(item,False)。
Queue.qsize()
返回Queue中目前的消息数量。这个结果并不一定准确,因为在返回结果直程序使用结果之间的时间内,Queue中的item数量可能已经变化,这可能会引发异常。
Queue.empty()
1)判断Queue是否为空,是空返回True,否则返回False。
2)同Queue.qsize(),该方法的结果也是不可靠的。
Queue.full()
1)判断Queue是否已满,是返回True,反之返回False。
2)同Queue.empty()。
Queue.close()
关闭Queue,调用该方法后无法再向Queue中添加数据,但曾经被堵塞的写入操作或者在调用close()之前但还未完成的操作仍将继续完成,Queue将在所有这些操作完成后立即关闭。
另外注意:初始化Queue时,可以设置Queue的最大可接受信息数量,如果未设置或者设置为负值,则表示可接受的信息数量无限制,直到全部内存被使用。
2.Queue实现进程间通信
我们知道进程的内存空间都是互相独立的,那么它们是如何用Queue实现通信的呢?看一个示例。
示例:
from multiprocessing import Process,Queue
def read(q):
print('Process is reading...')
while True:
if not q.empty():
value = q.get()
print('%s is successfully read in from Queue.' % (value))
else:
break
def write(q):
print('Process is writing...')
for value in "HELLO":
print('Put %s to Queue successfully.' % (value))
q.put(value)
if __name__ == '__main__':
q = Queue()
pr = Process(target=read,args=(q,))
pw = Process(target=write,args=(q,))
pw.start()
pw.join()
pr.start()
pr.join()
print('A successful communication.')
运行结果:
Process is writing...
Put H to Queue successfully.
Put E to Queue successfully.
Put L to Queue successfully.
Put L to Queue successfully.
Put O to Queue successfully.
Process is reading...
H is successfully read in from Queue.
E is successfully read in from Queue.
L is successfully read in from Queue.
L is successfully read in from Queue.
O is successfully read in from Queue.
A successful communication.
<分析>
示例中创建了两个子进程,pw和pr,可以看到我们用Queue实现了两个子进程间的通信。pw将数据写入Queue中,而pr将数据从Queue中读出,需要注意的就是在调用pw.start()时接着就要调用pw.join(),这样才能保证数据全部写入。如果要想实现父进程与子进程间的通信,只需要在主程序中调用get()、put()方法即可。
注意点:Queue同样遵循队列数据先入先出的规则;
Pipe
如果使用Pipe来实现进程通信,程序会调用multiprocessing.Pipe()函数创建一个管道,该函数会返回一个元组,元组中包括两个PipeConnection对象,代表管道的两个连接端,即管道连接的两个进程。
应当注意的是,Pipe()有默认参数duplex,它默认管道为全双工的,如果将duplex映射为False,那么PipeConnection对象1只能用于接收,PipeConnection对象2只能用于发送。
1.PipeConnection对象(设为pcn)的常用方法
pcn.send(obj)
1)发送一个obj对象给管道的另一端。该对象必须是与序列化兼容的,即可picklable的。
2)该方法发送的数据只能由recv()方法接收。
pcn.recv()
接受Pipe另一端通过send()方法发送过来的数据。
pcn.fileno()
返回关于连接所使用的整数文件描述符。
pcn.close()
关闭连接。如果pcn对象被垃圾回收,则自动调用此方法。
pcn.poll(timeout)
1)判断连接中是否还有数据可以读取,是返回True,否则返回False。
2)timeout指定等待的最长期限,如果忽略timeout,该方法将立即返回结果,如果timeout为None,该方法将无限期的等待数据到达。
pcn.send_bytes(buffer,offset,size)
1)发送字节缓冲区中的数据。
2)buffer是支持缓冲区接口的任意对象,如果未指定offset和size,则默认发送全部数据,如果指定,则发送buffer字节串中从offset位置开始,长度为size的字节数据。
3)该方法发送的数据只能由recv_bytes()方法接收。
pcn.recv_bytes(maxlength)
接受send_bytes()发送的数据并返回,maxlength用于指定最多接收的字节数。
pcn.recv_bytes_info(buffer,offset)
功能上与recv_bytes()方法类似,只不过该方法将接收到的数据放在buffer中。
2.Pipe实现进程间通信
示例:
from multiprocessing import Process,Pipe
def send(p,info):
print('Process send...')
for value in info:
print('Send %s ' % (value))
p.send(value)
def recv(p):
print('Process reveive...')
while True:
if p.poll():
print('recv %s ' % (p.recv()))
else:
break
if __name__ == '__main__':
p = Pipe()
ps = Process(target=send,args=(p[0],'HELLO'))
pr = Process(target=recv,args=(p[1],))
ps.start()
ps.join()
print()
pr.start()
pr.join()
print()
print('A successful communication.')
运行结果:
Process send...
Send H
Send E
Send L
Send L
Send O
Process reveive...
recv H
recv E
recv L
recv L
recv O
A successful communication.
<分析>
用Pipe实现进程间通信与使用Queue差不多,本例中创建了两个子进程并实现了它们的通信,这里注意的是用Pipe创建一个管道时返回的是一个元组,所以本例中的p[0]、p[1]是元组中的前两个成员,当然你也可以定义两个变量来分别接受Pipe()的返回值。