原文:http://blog.ftofficer.com/2009/12/python-multiprocessing-3-about-queue/
继续讨论Python multiprocessing,这次讨论的主要内容是mp库的核心组件之一的Queue。
Queue是mp库当中用来提供多进程对象交换的方式。对象交换和上一部分当中提到的对象共享都是使多个进程访问同一个对象的方式,两者的区别就是,对象共享是多个进程访问同一个对象,对象交换则是将对象从一个进程传输的另一个进程。
multiprocessing当中的Queue使用方式和Python内置的threading.Queue对象很像,它支持一个put操作,将对象放入Queue,也支持一个get操作,将对象从Queue当中读出。和threading.Queue不同的是,mp.Queue默认不支持join()和task_done操作,这两个支持需要使用mp.JoinableQueue对象。
由于Queue对象负责进程之间的对象传输,因此第一个问题就是如何在两个进程之间共享这个Queue对象本身。在上一部分所言的三种共享方式当中,Queue对象只能使用继承(inheritance)的方式共享。这是因为Queue本身基于unix的Pipe对象实现,而Pipe对象的共享需要通过继承。因此,在一个典型的应用实现模型当中,应该是父进程创建Queue,然后创建子进程共享该Queue,由父进程和子进程分别读写。例如下面的这个例子:
import multiprocessing
q = multiprocessing.Queue()
def reader_proc():
print q.get()
reader = multiprocessing.Process(target=reader_proc)
reader.start()
q.put(100)
reader.join()
另一种实现方式是父进程创建Queue,创建多个子进程,有的子进程读Queue,有的子进程写Queue,例如:
q = multiprocessing.Queue()
def writer_proc():
try:
q.put(1,block = False) #这个block的选项要加上,默认block为True,读不到就阻塞,会让人感觉像死锁了一样,而操作没有成功会抛出Queue.Full的异常,所以要处理一下
except:
pass
def reader_proc():
try:
q.get(block = False) #这个block的选项要加上,默认block为True,读不到就阻塞,会让人感觉像死锁了一样,而操作没有成功会抛出Queue.Full的异常,所以要处理一下
except:
pass
reader = multiprocessing.Process(target=reader_proc)
reader.start()
writer = multiprocessing.Process(target=writer_proc)
writer.start()
reader.join()
writer.join()
import multiprocessing.managers as mpm
import Queue
class SharedQueueManager(mpm.BaseManager): pass
q = Queue.Queue()
SharedQueueManager.register('Queue', lambda: q)
mgr = SharedQueueManager(address=('', 12345))
server = mgr.get_server()
server.serve_forever()
mgr = SharedQueueManager(address=('localhost', 12345))
mgr.connect()
q = mgr.Queue() # 这里q就是共享的Queue对象的代理对象
这种方式比起mp库内置的Queue,有一些性能上的影响,因为毕竟牵涉到多次网络通讯,但是带来的好处是没有feed线程带来的一系列问题,而且理论上不会存在丢数据的问题,除非server process崩溃。但是正如上一篇所说,server process本身就不是很靠谱的,因此这里也只是“理论上”不会丢数据而已。
说到性能,这里就列两个性能数据,以前在twitter上面提到过的(这两个连接无法访问的请联系我):
操作对象为 pickle后512字节的对象,通过proxy操作Queue的性能大约是7000次/秒(本机)或1100次/秒(多机),如果使用 multiprocessing.Queue,效率可达54000次/秒。