我实际上发现很难相信我遇到了我遇到的问题,好像它会成为python多处理模块中的一个大错误...无论如何我遇到的问题是每当我将multiprocessing.Queue作为参数传递给multiprocessing.Pool工作者时,池工作者永远不会执行其代码。即使是在python docs中找到的示例代码的略微修改版本的非常简单的测试,我也能够重现这个bug。
以下是队列示例代码的原始版本:
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
以下是我对队列的示例代码的修改版本:
from multiprocessing import Queue, Pool
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Pool(1)
p.apply_async(f,args=(q,))
print(q.get()) # prints "[42, None, 'hello']"
p.close()
p.join()
我所做的就是创建一个大小为1的进程池,而不是multiprocessing.Process对象,结果是代码永远挂在print语句上,因为没有任何东西写入Queue!当然,我以原始形式测试了它,它工作正常。我的操作系统是Windows 10,我的python版本是3.5.x,任何人都知道为什么会发生这种情况?
更新:仍然不知道为什么这个示例代码适用于multiprocessing.Process而不是multiprocessing.Pool,但我发现work around我很满意(Alex Martelli的回答)。显然你可以只创建一个multiprocessing.Queues的全局列表并传递每个进程和索引来使用,我将避免使用托管队列,因为它们较慢。感谢嘉宾给我看链接。
答案 0 :(得分:1)
当你致电apply_async
时,它会返回一个AsyncResult
对象,并将工作负载分配留给一个单独的线程(另请参阅this answer)。该线程遇到Queue
对象不能pickled的问题,因此无法分发(并最终执行)所请求的工作。我们可以通过调用AsyncResult.get
:
r = p.apply_async(f,args=(q,))
r.get()
引发RuntimeError
:
RuntimeError: Queue objects should only be shared between processes through inheritance
但是,只有在请求结果时才会在主线程中引发RuntimeError
,因为它实际上发生在不同的线程中(因此需要一种传输方式)。
那么当你做
时会发生什么p.apply_async(f,args=(q,))
是永远不会调用目标函数f
,因为其中一个参数(q
)无法被腌制。因此q
永远不会收到一个项目并保持为空,因此主线程中对q.get
的调用将永久阻止。
使用apply_async
您不必手动管理结果队列,但很容易以AsyncResult
个对象的形式提供给您。因此,您可以修改代码以简单地从目标函数返回:
from multiprocessing import Queue, Pool
def f():
return [42, None, 'hello']
if __name__ == '__main__':
q = Queue()
p = Pool(1)
result = p.apply_async(f)
print(result.get())