所以,我有一个问题,我认为这个问题必须是常见的:
我想将脚本与multiprocessing.Pool
并行化,将输入交给池,让它并行处理,并在父进程中接收输出。
apply_async()
看起来最适合我想做的事情。但我不能只给出一个回调函数,因为最后我想将所有结果打印到一个文件中。我认为将一个回调打印到单个文件句柄会导致混乱的结果(甚至不能确定我可以在这样的进程之间传递文件句柄)。
那么如何向Pool
提交输入,然后接收输出并在主进程中处理它们的最佳方法是什么?目前我只是在列表中收集AsyncResult
个对象并定期迭代它,在每个对象上调用 .get()
方法。
我会在回应评论时澄清我的问题的一些参数:
@martineau和@Juggernaut:不是“混乱”,我的意思是我真的想保留输入的顺序,以便输出的顺序相同。
@RolandSmith和@martineau:我的主要过程就是从文件读取输入,将它们交给池,然后打印结果。我可以调用 .apply()
,但是主进程正在等待函数完成,然后再继续。我正在使用 multiprocessing
来获得并行化的好处,同时处理许多输入。
答案 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()