multiprocessing.Queue as arg to pool worker中止worker的执行

时间:2017-07-18 23:01:32

标签: python python-3.x python-multiprocessing

我实际上发现很难相信我遇到了我遇到的问题,好像它会成为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的全局列表并传递每个进程和索引来使用,我将避免使用托管队列,因为它们较慢。感谢嘉宾给我看链接。

1 个答案:

答案 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())