多处理池和队列

时间:2016-01-13 16:29:46

标签: python python-2.7 multiprocessing pool

我正在使用池进行多处理。我需要将结构作为参数传递给必须在单独进程中使用的函数。我遇到multiprocessing.Pool的映射函数问题,因为我既不能复制Pool.Queue也不能复制Pool.Array。该结构将在运行中用于记录每个终止进程的结果。这是我的代码:

import multiprocessing
from multiprocessing import Process, Manager, Queue, Array
import itertools
import time

def do_work(number, out_queue=None):
    if out_queue is not None:
        print "Treated nb ", number
        out_queue.append("Treated nb " + str(number))
    return 0


def multi_run_wrapper(iter_values):
    return do_work(*iter_values)

def test_pool():
    # Get the max cpu
    nb_proc = multiprocessing.cpu_count()

    pool = multiprocessing.Pool(processes=nb_proc)
    total_tasks = 16
    tasks = range(total_tasks)

    out_queue= Queue()  # Use it instead of out_array and change out_queue.append() into out_queue.put() in the do_work() function.
    out_array = Array('i', total_tasks)
    iter_values = itertools.izip(tasks, itertools.repeat(out_array))
    results = pool.map_async(multi_run_wrapper, iter_values)

    pool.close()
    pool.join()
    print results._value
    while not out_queue.empty():
        print "queue: ", out_queue.get()
    print "out array: \n", out_array

if __name__ == "__main__":
    test_pool()

我需要在分离进程中启动一个worker并将我的输出队列作为参数传递。我还想指定包含有限数量的正在运行的进程的池。为此我使用pool.map_async()函数。不幸的是,上面的代码给了我一个错误:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 808, in __bootstrap_inner
    self.run()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 761, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 342, in _handle_tasks
    put(task)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/queues.py", line 77, in __getstate__
    assert_spawning(self)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/forking.py", line 52, in assert_spawning
    ' through inheritance' % type(self).__name__
RuntimeError: Queue objects should only be shared between processes through inheritance

我认为这是因为我在文档中读到的Queue无法复制。 然后我想把队列变成一个全局变量,这样我就不需要再传递它了,但在我看来这会很混乱。我还想过使用multiprocessing.Array代替

out_array = Array('i', total_tasks)

但是同样的错误会像队列一样上升:

# ...
RuntimeError: SynchronizedArray objects should only be shared between processes through inheritance

我需要使用此功能 - 使用多处理和交换子进程的信息 - 在一个相对较大的软件中,所以我希望我的代码保持干净整洁。

如何以优雅的方式将队列传递给我的工作人员?

当然,欢迎任何其他处理主要规范的方式。

1 个答案:

答案 0 :(得分:4)

multiprocessing.Pool不会在其工作队列中接受multiprocessing.Queue作为参数。我相信这是因为它在内部使用队列来回传输数据到工作进程。有几种解决方法:

1)你真的需要使用队列吗? Pool函数的一个优点是它们的返回值被发送回主进程。通常,最好迭代池中的返回值,而不是使用单独的队列。这也可以通过检查queue.empty()

来避免引入竞争条件

2)如果您必须使用Queue,则可以使用multiprocessing.Manager中的Pool。这是共享队列的代理,可以作为参数传递给Queue函数。

3)在创建Pool(如initializer)时,您可以使用https://stackoverflow.com/a/3843313将正常while not out_queue.empty(): print "queue: ", out_queue.get() 传递给工作进程。这有点像黑客。

竞争条件我上面提到的来自:

.empty()

当您的工作进程填满队列时,您可以拥有队列当前为空的条件,因为工作人员正要将某些内容放入其中。如果您此时检查var displayValue: Double { get { return NSNumberFormatter().numberFromString(display.text!)!.doubleValue } set { display.text = String(format: "%g", newValue) } } ,您将提前结束。一种更好的方法是将 sentinal 值放入队列中,以便在完成数据输入后发出信号。