【发布时间】:2021-04-02 18:29:20
【问题描述】:
我正在使用 ThreadPoolExecutor 同时执行多个任务,这个问题是关于优雅地取消未完成的任务。 在我看来,当我取消未完成(即尚未运行)的期货时,concurrent.futures.as_completed 永远不会产生取消的期货。尽管它的文档声明“[as_completed] 在完成时产生期货(已完成或取消的期货)。”
例子:
import concurrent.futures
import time
def do_task(t):
time.sleep(t)
print('task',t,'finished')
return t
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
future_to_t = {executor.submit(do_task, t): t for t in range(1,6)}
for future in concurrent.futures.as_completed(future_to_t):
print('tasks done?',[f.done() for f in future_to_t])
t = future_to_t[future]
if t == 1:
executor.shutdown(wait=False, cancel_futures=True)
print('outstanding tasks are now cancelled')
print('all tasks done')
print('bye')
在示例中,我将 5 个任务放入管道中,并在第一个任务完成后取消它们。由于任务 2、3 和 4 已经开始(3 个工作人员),我希望任务 5 被取消并由 as_completed 产生——但这永远不会发生。上面的代码打印:
task 1 finished
tasks done? [True, False, False, False, False]
outstanding tasks are now cancelled
task 2 finished
tasks done? [True, True, False, False, True]
task 3 finished
tasks done? [True, True, True, False, True]
task 4 finished
tasks done? [True, True, True, True, True]
然后它挂起并且永远不会到达all tasks done。
我可以通过检查期货的状态并在所有期货完成后打破循环来解决这个问题 - 但这是我对 as_completed 的期望。
我做错了什么?有什么见解吗?谢谢!
跟进:
- 如果我使用
ThreadPoolExecutor.shutdown()取消期货,它们的状态将更改为CANCELLED,而不是CANCELLED_AND_NOTIFIED,正如concurrent.futures.as_completed()所期望的那样。 -
如果我自己取消期货,
concurrent.futures.as_completed()将按预期工作。 - 因此,作为一种解决方法,我使用
for f in future_to_t: f.cancel()而不是executor.shutdown(wait=False, cancel_futures=True)
【问题讨论】:
标签: python python-3.x multithreading concurrency concurrent.futures