如何从多处理队列继承?

时间:2013-09-19 23:28:12

标签: python python-2.7 queue multiprocessing

使用以下代码,似乎传递给worker的队列实例未初始化:

from multiprocessing import Process
from multiprocessing.queues import Queue

class MyQueue(Queue):

    def __init__(self, name):
        Queue.__init__(self)
        self.name = name

def worker(queue):
    print queue.name

if __name__ == "__main__":
    queue = MyQueue("My Queue")
    p = Process(target=worker, args=(queue,))
    p.start()
    p.join()

这引发:

... line 14, in worker
    print queue.name
AttributeError: 'MyQueue' object has no attribute 'name'

我无法重新初始化队列,因为我会丢失queue.name的原始值,甚至将队列的名称作为参数传递给worker(这应该可行,但它不是一个干净的解决方案)。

那么,如何从multiprocessing.queues.Queue继承而不会出现此错误?

1 个答案:

答案 0 :(得分:4)

在POSIX上,通过简单继承将Queue个对象共享给子进程。*

在Windows上,这是不可能的,所以它必须挑选Queue,通过管道将其发送给孩子,然后取消它。

(这可能不是很明显,因为如果你真的尝试挑选Queue,你会得到一个例外,RuntimeError: MyQueue objects should only be shared between processes through inheritance。如果你仔细查看来源,你会发现这是真的谎言 - 如果你在Queue没有产生子进程的时候试图挑选multiprocess,那么它只会引发这个例外。)

当然,通用的pickle和unpickling不会有任何好处,因为你最终会得到两个相同的队列,而不是两个进程中的相同队列。因此,multiprocessing通过为unpickling中使用的对象添加register_after_fork机制来扩展一些内容。**如果查看the source for Queue,您可以看到它是如何工作的。

但是你真的不需要知道如何它可以挂钩它;你可以像任何其他类的酸洗一样挂钩它。例如,这应该有效:***

def __getstate__(self):
    return self.name, super(MyQueue, self).__getstate__()

def __setstate__(self, state):
    self.name, state = state
    super(MyQueue, self).__setstate__(state)

有关详细信息,pickle文档说明了影响课程腌制方式的不同方法。

(如果它不起作用,我没有犯一个愚蠢的错误......那么你必须至少知道它是如何工作来挂钩它...但很可能只是找出是否在_after_fork()之前或之后做额外的工作,这只需要交换最后两行......)


*我不确定它实际上保证在POSIX平台上使用简单的fork继承。在2.7和3.3上恰好是这样。但是有一个multiprocessing的分支使用Windows风格的pickle-所有平台上的所有内容保持一致性,另一个使用OS X上的混合允许在单线程模式下使用CoreFoundation,或者类似的东西,这显然是可行的。

**实际上,我认为 Queue只是为了方便而使用register_after_fork,并且可以在没有它的情况下重写......但它取决于{{1}的魔力}在Windows上的Pipe或POSIX上的_after_forkLock

***这只是正确的,因为我从阅读来源开始知道BoundedSemaphore是新式的类,不会覆盖Queue__reduce__,并且永远不会从__reduce_ex返回虚假值。如果您不知道,那么您必须编写更多代码。