改变Python的多处理引发错误的方式

时间:2016-07-13 01:20:04

标签: python multithreading python-3.x multiprocessing

我使用pool.apply_async在Python中使用多处理,同时运行具有各种不同参数的函数。

代码的相关摘录是:

import multiprocessing as mp

all_details_to_process_full = [[x,y.z], [x2,y2.z2]] 

def loop_over_desired_sub(arg_list):
    ...

if __name__ == '__main__':   
   pool = mp.Pool(processes=10) 
   desired_content = [pool.apply_async(loop_over_desired_sub, args=(arg_list,)) for arg_list in all_details_to_process_full]    
   results = [p.get() for p in desired_content]

据我所知,默认行为是Python只在最初初始化的子进程引发错误时停止代码。

例如,如果列表中有10个项目要处理单独的子进程,并且处理第一个项目(即初始化的第一个子进程)时出错,Python将立即引发错误,停止代码。但是,如果第二个子进程中存在错误,而该子进程将停止,则其余代码将继续执行,直到第一个项目完成,此时错误将被引发并且代码停止。 [如果在处理第三项时出现错误,则在引发错误之前,第一项和第二项都需要完成]。

是否有办法改变这种行为,包括:

  1. 引发任何错误,即在任何子进程中,以停止代码 立即

  2. 如果出现错误,代码不会停止,直到全部 子处理已完成

1 个答案:

答案 0 :(得分:2)

当您使用apply_async时,您的每个流程都是独立的。因此,python的默认行为是独立处理它们,这意味着一个失败不会影响另一个'

这里的问题是您以有序的方式处理函数loop_over_desired_content的结果。 get方法将被阻塞,直到检索到第一个操作的结果(即使第二个进程返回/失败)。然后,它将处理第二个值,如果需要则引发错误。

import multiprocessing as mp
import time


def fail_in(args):
    x, l = args
    if x == l:
        raise RuntimeError(x)
    time.sleep(.5)
    print("Finish process {}".format(x))
    return x


if __name__ == '__main__':
    pool = mp.Pool(processes=3)
    tasks = [(i, 0) for i in range(9)]

    try:
        desired_content = [pool.apply_async(fail_in, args=(a,)) for a in tasks]
        t1 = time.time()
        results = [p.get() for p in desired_content]
    except RuntimeError:
        print("apply_async 0 failed in {:4.2}s".format(time.time()-t1))
    pool.terminate()
    pool = mp.Pool(processes=3)
    tasks = [(i, 1) for i in range(9)]

    try:
        desired_content = [pool.apply_async(fail_in, args=(a,)) for a in tasks]
        t1 = time.time()
        results = [p.get() for p in desired_content]
    except RuntimeError:
        print("apply_async 1 failed in {:4.2}s".format(time.time()-t1))
    pool.terminate()
    pool = mp.Pool(processes=3)
    tasks = [(i, 4) for i in range(9)]

    try:
        desired_content = [pool.apply_async(fail_in, args=(a,)) for a in tasks]
        t1 = time.time()
        results = [p.get() for p in desired_content]
    except RuntimeError:
        print("apply_async 4 failed in {:4.2}s".format(time.time()-t1))
    pool.terminate()

请注意,此错误不会终止剩余进程。您可以通过尝试在池中提交新作业而不使用terminate来查看它。他们将在您上一份工作的所有剩余流程完成后启动。

要获得更快的错误通知,您可以使用方法imap_unordered,这会在错误返回时立即引发错误。您需要小心,因为您需要使用job_id来找回订单 在这种情况下,您还可以使用callback_error获取通知以执行清理。

对于第二个行为者,在提出错误之前要求所有结果进行处理,您可以使用:

desired_content = [pool.apply_async(loop_over_desired_sub, args=(arg_list,)) 
                   for arg_list in all_details_to_process_full]
results = []
for p in desired_content:
    try:
        r = p.get()
    except Exception as r:
        pass
    results += [r]

results = [p.get() for p in desired_content]