如何通过异常正确取消Python ThreadPoolExecutor中的长耗时任务
问题原因分析
你现有代码无法实现预期效果的核心原因有两个:
- 线程池通过
submit提交的任务抛出异常时,异常只会存储在返回的future对象内部,你没有主动调用future.result()或者future.exception()方法的话,异常不会冒泡到主线程,自然无法被调用侧的try-except捕获 - 现有逻辑会等
some_very_long_task完全执行完成后才会操作取消检测的future,就算取消条件提前触发,也不会中断正在运行的长耗时任务,取消逻辑完全不生效
修改方案
我们需要同时监听「长耗时任务完成」和「取消条件触发」两个事件,哪个先触发就优先处理哪个:
- 将长耗时任务也提交到线程池,和取消检测任务同时运行
- 使用
concurrent.futures.wait等待两个任务,只要有一个任务完成就终止流程 - 如果是取消检测任务先完成,说明触发了取消条件,直接抛出取消异常,同时终止长耗时任务
- 如果是长耗时任务先完成,直接返回结果即可
注意:Python 标准线程无法被强制终止,如果
some_very_long_task是你可修改的自有逻辑,建议在内部定期判断取消标志位;如果是调用外部系统进程,检测到取消时直接调用进程的terminate()方法终止即可。
可运行的修改后代码
from concurrent.futures import ThreadPoolExecutor, wait, FIRST_COMPLETED def cancel_task(self): while True: process = get_process() if process.status == "CANCELLED": # 正常返回即代表取消条件触发 return True time.sleep(1) def execute(self): with ThreadPoolExecutor(max_workers=2) as executor: # 同时提交取消检测任务和长耗时任务 cancel_future = executor.submit(self.cancel_task) task_future = executor.submit(some_very_long_task) # 等待任意一个任务先完成 done, _ = wait([cancel_future, task_future], return_when=FIRST_COMPLETED) # 取消检测任务先完成,触发取消流程 if cancel_future in done: # 尝试取消长耗时任务:未启动的任务会直接取消,已运行的任务需要内部逻辑支持终止 task_future.cancel() # 抛出取消异常,供外部捕获 raise OperationCancelledException() # 长耗时任务先完成,终止取消检测任务 cancel_future.cancel() # 返回长任务结果,长任务自身抛出的异常也会通过result()正常冒泡 return task_future.result()
调用侧代码无需做任何修改,即可正常捕获OperationCancelledException异常。
内容的提问来源于stack exchange,提问作者Nickon
相关产品推荐
相关产品推荐

