使用以下代码,似乎传递给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继承而不会出现此错误?
答案 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_fork
和Lock
。
***这只是正确的,因为我从阅读来源开始知道BoundedSemaphore
是新式的类,不会覆盖Queue
或__reduce__
,并且永远不会从__reduce_ex
返回虚假值。如果您不知道,那么您必须编写更多代码。