Python 使用多处理时,是否设置池映射操作的时间限制?
在Python中使用Python 使用多处理时,是否设置池映射操作的时间限制?,python,multiprocessing,Python,Multiprocessing,在Python中使用多处理时,是否可以对pool map()操作设置时间限制。当达到时间限制时,所有子进程将停止并返回它们已有的结果 import multiprocessing as mp def task(v): do something return result if __name__ == '__main__': vs = [...] p= mp.Pool() results = p.map(task, vs) 在上面的例子中,我有一个非常
多处理
时,是否可以对pool map()
操作设置时间限制。当达到时间限制时,所有子进程将停止并返回它们已有的结果
import multiprocessing as mp
def task(v):
do something
return result
if __name__ == '__main__':
vs = [...]
p= mp.Pool()
results = p.map(task, vs)
在上面的例子中,我有一个非常大的列表vs
。理想情况下,列表vs
中的所有元素都将被发送到函数task()
,所有结果都将保存在results
列表中
然而,由于列表vs
非常大,我只有有限的时间来执行这个过程(比如5分钟)。我需要的是在达到5分钟时停止map
过程,并将计算结果返回到列表results
EDIT1:
我不会放弃一项需要5分钟以上才能完成的任务。假设列表中有1000个任务,5分钟后只完成600个任务。我需要的是杀死所有的子进程,并将这600个任务的结果保存到结果
列表中。我看过,它使用了一种叫做Pebble
的东西。首先,我不理解关于“Python标准池不支持超时”的评论。它的方式是,您可以等待指定的时间量以返回结果,并通过异常通知它是否存在超时,或者您可以对指定超时的结果对象发出等待。在这种情况下不会返回异常,但您可以测试处理结果的“作业”是否已完成。但是,您确实不能终止单个超时作业。但是,当处理完所有未超时的结果后,可以对池本身调用terminate
,这将终止池中的所有进程,无论它们是否空闲。这导致了回答中的第二条评论,“突然终止进程可能会导致应用程序中出现奇怪的行为。”这取决于超时的作业所做的工作。因此,我们同意,如果这样做可能导致奇怪的行为,我们不应该过早地暂停工作和终止工作。但我看不出Pebble
如何更好地处理这个问题
这个问题的答案是答案,实际上有一种技巧可以让你做你想做的事情。您需要放弃使用map
函数,转而使用apply\u async
指定回调函数,以便在结果可用时保存结果。在下面的示例中,我使用5秒的超时值只是为了演示,并且已经安排了我提交给超时的10个作业中的大约一半。我已经预先分配了一个名为squares
的结果列表,该列表将保存10个结果,并且已经用10个None
值初始化。当我们都完成时,如果第i个值是None
,那是因为处理值i
的作业超时了。My workder函数还返回其参数v
,以及其计算值v**2
,以便回调函数知道计算结果应位于正方形列表中的哪个位置:
import multiprocessing as mp
import time
def my_task(v):
time.sleep(v)
return v, v ** 2
squares = [None] * 10
def my_callback(t):
i, s = t
squares[i] = s
TIMEOUT = 5
if __name__ == '__main__':
vs = range(10)
pool = mp.Pool()
results = [pool.apply_async(my_task, args=(v,), callback=my_callback) for v in vs]
time.sleep(TIMEOUT)
pool.terminate() # all processes, busy or idle, will be terminated
print(squares)
印刷品:
[0, 1, 4, 9, 16, None, None, None, None, None]
Timeout for v = 0
Return value for v = 1 is 1
Return value for v = 2 is 4
Return value for v = 3 is 9
Return value for v = 4 is 16
Timeout for v = 5
Timeout for v = 6
第二个更复杂的方法不使用回调函数。而是对调用池返回的每个AsynchResult
实例执行get
调用。apply\u async
指定超时值。这里需要技巧的是,对于初始调用,您必须使用完整的超时值。但当结果返回或出现超时异常时,您已经等待了一段时间,t
。这意味着您下次获得超时结果时,您指定的超时值应减少t
:
import multiprocessing as mp
import time
def my_task(v):
time.sleep(6 if v == 0 else v)
return v ** 2
TIMEOUT = 5
if __name__ == '__main__':
vs = range(mp.cpu_count() - 1) # 7 on my desktop
pool = mp.Pool() # poolsize is 8
results = [pool.apply_async(my_task, args=(v,)) for v in vs]
time_to_wait = TIMEOUT # initial time to wait
start_time = time.time()
for i, result in enumerate(results):
try:
return_value = result.get(time_to_wait) # wait for up to time_to_wait seconds
except mp.TimeoutError:
print('Timeout for v = ', i)
else:
print(f'Return value for v = {i} is {return_value}')
# how much time has exprired since we began waiting?
t = time.time() - start_time
time_to_wait = TIMEOUT - t
if time_to_wait < 0:
time_to_wait = 0
pool.terminate() # all processes, busy or idle, will be terminated
注意
通过使用apply\u async
而不是map
,作业将以1的chunksize有效提交(请参阅chunksize
的map
参数,该参数确定iterable参数如何分解为“chunks”放在每个进程的输入队列上,以最大限度地减少共享内存传输的数量。对于大型iterables,apply\u async
可能比map
效率低,后者使用“合理的”默认的chunksize基于您的池大小和要处理的作业数。这看起来很相似:是什么让您认为重新发布已结束问题的确切副本会给您带来不同的结果。[对于超过10K:@PM77-1,我编辑了已关闭的帖子,但我注意到它仍处于关闭状态,其他人可能看不到它,所以我重新发布here@ForceBru我会检查的。感谢这两种方法对我都有效。我对第二种方法有顾虑。在函数“我的任务”(v)中,我使用时间。睡眠(6)如果v==0,其他变量保持不变。在这种情况下,v为1-4的任务可以完成。在结果检索步骤中,它被设计成有序地检索结果。因此,v==0将消耗所有的等待时间,让其他变量等待的时间为0。虽然在我的笔记本电脑上,v=1到4的结果可以在等待时间为0的情况下成功检索,但我是同事当我们从等待时间为0的已完成任务中获得结果时,尤其是当硬件陈旧且速度缓慢时,它是否总是有效。如果您有if v==0:time.sleep(6)
但没有其他作业休眠,则如果提交的作业数为,但作业数大于池大小,则第一个进程队列中至少有一个作业在第一个作业完成之前不会计划运行。因此,这些作业也将超时。这两种方法都适用。我已更新了第二个编码示例:当v
为0时,让my_task
睡眠6秒,否则它将像以前一样睡眠v
秒