Python 如何正确处理多处理中的异常

Python 如何正确处理多处理中的异常,python,multiprocessing,python-multiprocessing,Python,Multiprocessing,Python Multiprocessing,可以使用Pool.map检索worker的输出,但当一个worker失败时,会引发异常,无法再检索输出。因此,我的想法是将输出记录在进程同步队列中,以便检索所有成功工作人员的输出 以下代码段似乎有效: from multiprocessing import Pool, Manager from functools import partial def f(x, queue): if x == 4: raise Exception("Error")

可以使用Pool.map检索worker的输出,但当一个worker失败时,会引发异常,无法再检索输出。因此,我的想法是将输出记录在进程同步队列中,以便检索所有成功工作人员的输出

以下代码段似乎有效:

from multiprocessing import Pool, Manager
from functools import partial

def f(x, queue):
    if x == 4:
        raise Exception("Error")

    queue.put_nowait(x)

if __name__ == '__main__':
    queue = Manager().Queue()
    pool = Pool(2)

    try:
        pool.map(partial(f, queue=queue), range(6))
        pool.close()
        pool.join()
    except:
        print("An error occurred")

    while not queue.empty():
        print("Output => " + str(queue.get()))
但我想知道在队列轮询阶段是否会出现竞争条件。我不确定当所有工作人员都完成时,队列进程是否一定是活动的。从这个角度来看,您认为我的代码正确吗?

至于“如何正确处理异常”,这是您的主要问题:

首先,在您的情况下,您将永远无法执行
pool.close
pool.join
。但是
pool.map
在所有提交的任务返回结果或生成异常之前不会返回,因此您确实不需要调用它们来确保所有提交的任务都已完成。如果不是工作函数
f
将结果写入队列,那么只要您的任何任务导致异常,您就永远无法使用
map
返回任何结果。相反,您必须
应用\u async
单个任务,并为每个任务获取
AsyncResult
实例

因此,我想说,在不必使用队列的情况下处理工作函数中异常的更好方法如下。但是请注意,当您使用
apply_async
时,任务一次提交一个任务,这可能会导致许多共享内存访问。只有当提交的任务数量非常大时,这才真正成为性能问题。在这种情况下,工作函数最好自己处理异常,并以某种方式传回错误指示,以允许使用
map
imap
,您可以在其中指定
chunksize

使用队列时,请注意,写入受管队列会产生相当大的开销。第二段代码展示了如何通过使用
多处理.Queue
实例来减少开销,该实例不像托管队列那样使用代理。请注意输出顺序,它不是任务提交的顺序,而是任务完成的顺序——使用队列的另一个潜在缺点或优点(如果希望结果按顺序完成,可以使用带有
apply\u async
的回调函数)。即使使用原始代码,也不应依赖于队列中结果的顺序

来自多处理导入池的

def f(x):
如果x==4:
引发异常(“错误”)
返回x
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
池=池(2)
结果=[pool.apply_async(f,args=(x,))用于范围(6)内的x]
对于x,枚举(结果)中的结果:#结果是AsyncResult实例:
尝试:
return_value=result.get()
除:
打印(x={x}'发生f'错误)
其他:
打印(f'For x={x}返回值为{return\u value}')
印刷品:

For x = 0 the return value is 0
For x = 1 the return value is 1
For x = 2 the return value is 2
For x = 3 the return value is 3
An error occurred for x = 4
For x = 5 the return value is 5
An error occurred
Output => 0
Output => 2
Output => 3
Output => 1
Output => 5
OP的原始代码已修改为使用多处理.Queue

来自多处理导入池、队列的

def初始池(q):
全局队列
队列=q
def f(x):
如果x==4:
引发异常(“错误”)
队列。放置_nowait(x)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
队列=队列()
pool=pool(2,初始值设定项=init_pool,initargs=(队列,)
尝试:
池图(f,范围(6))
除:
打印(“发生错误”)
而不是queue.empty():
打印(“Output=>”+str(queue.get()))
印刷品:

For x = 0 the return value is 0
For x = 1 the return value is 1
For x = 2 the return value is 2
For x = 3 the return value is 3
An error occurred for x = 4
For x = 5 the return value is 5
An error occurred
Output => 0
Output => 2
Output => 3
Output => 1
Output => 5

我觉得一切都很好。当您完成
SyncManager
实例(通过调用
Manager()
返回)及其代理(例如,通过调用
Manager().Queue()
)返回)时,如果要停止管理器的进程,您可以在使用管理器退出
时显式或隐式地对其调用
shutdown()
()作为经理:
block。但是,由于您在读完队列后立即终止,因此无论采用哪种方式都无关紧要。非常感谢您给出了清晰详细的答案!