如何使用multiprocessing.Pool异步没有回调?

时间:2017-10-19 15:50:23

标签: python python-2.7 parallel-processing

所以,我有一个问题,我认为这个问题必须是常见的:

我想将脚本与multiprocessing.Pool并行化,将输入交给池,让它并行处理,并在父进程中接收输出。

apply_async() 看起来最适合我想做的事情。但我不能只给出一个回调函数,因为最后我想将所有结果打印到一个文件中。我认为将一个回调打印到单个文件句柄会导致混乱的结果(甚至不能确定我可以在这样的进程之间传递文件句柄)。

那么如何向Pool提交输入,然后接收输出并在主进程中处理它们的最佳方法是什么?目前我只是在列表中收集AsyncResult个对象并定期迭代它,在每个对象上调用 .get() 方法。

更新

我会在回应评论时澄清我的问题的一些参数:

  1. @martineau和@Juggernaut:不是“混乱”,我的意思是我真的想保留输入的顺序,以便输出的顺序相同。

  2. @RolandSmith和@martineau:我的主要过程就是从文件读取输入,将它们交给池,然后打印结果。我可以调用 .apply() ,但是主进程正在等待函数完成,然后再继续。我正在使用 multiprocessing 来获得并行化的好处,同时处理许多输入。

3 个答案:

答案 0 :(得分:2)

为了回答你的问题,我不认为你可以在没有回调的情况下做你想做的事。

您希望异步计算结果,但是以与输入相同的顺序打印。这意味着不仅必须等到输入所有输入之前,还要知道它们在输入中的相对位置,以便在输出之前将它们排序回该顺序。

所以这里是如何做到一个。正如我之前所说,稍微棘手的部分是结果必须包含指示输入的相应位置的东西,因此结果可以在打印之前以类似的顺序排序。由于该要求,脚本必须等到 all 输入已处理完毕。

请注意,尽管如此, 正在获得并行处理的好处,因为单个结果本身是由并发进程创建的。

import multiprocessing as mp
from random import randint
from time import sleep

def my_func(*args):
    print('my_func:', args)
    index, x = args
    sleep(randint(1, 3))  # Take a varying amount of time to finish.
    return index, x*x  # Return result index and value.

if __name__ == '__main__':

    result_list = []

    def errorhandler(exc):
        print('Exception:', exc)

    def log_result(result):
        # This is called whenever my_func() returns a result.
        # result_list is modified only by the main process, not the pool workers.
        result_list.append(result)

    pool = mp.Pool()
    for i in range(10):
        pool.apply_async(my_func, args=(i, i*2), callback=log_result,
                         error_callback=errorhandler)
    pool.close()
    pool.join()  # Wait for all subprocesses to finish.

    print('result_list:', result_list)
    sorted_results = [x[1] for x in sorted(result_list)]
    print('sorted results:', sorted_results)

输出:

my_func: (5, 10)
my_func: (1, 2)
my_func: (4, 8)
my_func: (7, 14)
my_func: (3, 6)
my_func: (9, 18)
my_func: (0, 0)
my_func: (6, 12)
my_func: (2, 4)
my_func: (8, 16)
result_list: [(2, 16), (3, 36), (5, 100), (1, 4), (4, 64), (7, 196), (9, 324), (0, 0), (6, 144), (8, 256)]
sorted results: [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]

答案 1 :(得分:2)

正如您在评论中所要求的,这里的代码显示如何使用Pool.map()代替Pool.async() - 这似乎更适合此问题,因为需要等待对于所有结果,可以进行进一步的输出处理(例如,它需要与输入的顺序相同)。

正如您所看到的,它需要显着更少的代码,并且在输出结果之前不需要对结果进行排序(因此也可能更快)。

import multiprocessing as mp
from random import randint
from time import sleep

def my_func(x):
    print('my_func:', x)
    sleep(randint(1, 3))  # Take a varying amount of time to finish.
    return x*x

if __name__ == '__main__':

    input_data = range(10)
    with mp.Pool(10) as pool:
        result_list = pool.map(my_func, input_data)  # Blocks until finished.

    print('result_list:', result_list)  # Will be in same order as input_data.

输出:

my_func: 3
my_func: 2
my_func: 1
my_func: 0
my_func: 8
my_func: 5
my_func: 7
my_func: 6
my_func: 4
my_func: 9
result_list: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

答案 2 :(得分:0)

  

我认为将一个回调打印到一个文件句柄就可以了   导致结果混乱

解决方案是使用结果填充回调中的Queue,然后再获取它们。队列是线程安全的,因此您不必担心您所谈论的混乱结果。

from queue import Queue
results = Queue()

def callback(result):
    results.put(result)

item = results.get()