繁体   English   中英

如何立即重新引发在任何辅助线程中引发的异常?

[英]How to immediately re-raise an exception thrown in any of the worker threads?

我正在使用ThreadPoolExecutor ,需要中止整个计算,以防任何工作线程失败。

示例1.由于ThreadPoolExecutor不会自动重新引发异常,因此无论错误如何,都将打印成功

from concurrent.futures import ThreadPoolExecutor

def task():
    raise ValueError

with ThreadPoolExecutor() as executor:
    executor.submit(task)
print('Success')

示例2。由于.result()重新引发异常,因此这.result()主线程正确崩溃。 但是它等待第一个任务完成,因此主线程会延迟地经历异常。

import time
from concurrent.futures import ThreadPoolExecutor

def task(should_raise):
    time.sleep(1)
    if should_raise:
        raise ValueError

with ThreadPoolExecutor() as executor:
    executor.submit(task, False).result()
    executor.submit(task, True).result()
print('Success')

如何在发生异常后立即在主线程中(或多或少)注意到工作程序异常,以处理故障并中止其余工作程序?

首先,我们必须先提交任务,然后再请求结果。 否则,线程甚至无法并行运行:

futures = []
with ThreadPoolExecutor() as executor:
    futures.append(executor.submit(good_task))
    futures.append(executor.submit(bad_task))
for future in futures:
    future.result()

现在,我们可以将异常信息存储在主线程和工作线程都可用的变量中:

exc_info = None

主线程不能真正杀死其子进程,因此我们让工作人员检查要设置的异常信息并停止:

def good_task():
    global exc_info
    while not exc_info:
        time.sleep(0.1)

def bad_task():
    global exc_info
    time.sleep(0.2)
    try:
        raise ValueError()
    except Exception:
        exc_info = sys.exc_info()

在所有线程终止之后,主线程可以检查包含异常信息的变量。 如果已填充,则重新引发异常:

if exc_info:
    raise exc_info[0].with_traceback(exc_info[1], exc_info[2])
print('Success')

我想,我会这样实现:

在主要过程中,我创建了2个队列:

  1. 报告异常的一个
  2. 一个通知取消。

::

import multiprocessing as mp

error_queue = mp.Queue()
cancel_queue = mp.Queue()

我创建每个ThreadPoolExecutor ,并将这些队列作为参数传递。

class MyExecutor(concurrent.futures.ThreadPoolExecutor):
    def __init__(self, error_queue, cancel_queue):
        self.error_queue : error_queue
        self.cancel_queue = cancel_queue

每个ThreadPoolExecutor都有一个主循环。 在此循环中,我首先扫描cancel_queue以查看是否有“取消”消息。

在主循环中,我还实现了一个异常管理器。 如果发生错误,我会提出一个例外:

self.status = "running"
with True:  # <- or something else
    if not self.cancel_queue.empty():
        self.status = "cancelled"
        break
    try:
        # normal processing
        ...
    except Exception as exc:
        # you can log the exception here for debug
        self.error_queue.put(exc)
        self.status = "error"
        break
    time.sleep(.1)

在主要过程中:

运行所有MyExecutor实例。

扫描error_queue:

while True:
    if not error_queue.empty():
        cancel_queue.put("cancel")
    time.sleep(.1)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM