Python 3.x 如何在ThreadPoolExecutor超时后退出脚本

Python 3.x 如何在ThreadPoolExecutor超时后退出脚本,python-3.x,python-multithreading,Python 3.x,Python Multithreading,我试图在一些工作超时后完全退出,因为某个线程被某个东西阻塞了。我使用的是ThreadPoolExecutor,如下所示: try: with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor: # submit some work workers = [executor.submit(...) for x in work] # wait for completion try:

我试图在一些工作超时后完全退出,因为某个线程被某个东西阻塞了。我使用的是
ThreadPoolExecutor
,如下所示:

try:
  with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor:
    # submit some work
    workers = [executor.submit(...) for x in work]
    # wait for completion
    try:
      for f in concurrent.futures.as_completed(workers, timeout=60):
        f.result()
    except concurrent.futures.TimeoutError:
      raise TimeoutError()
except TimeoutError:
  # cleanup
这段代码到达
#cleanup
没有问题,但是脚本永远不会退出,因为它正在等待被阻止的线程最终完成。我还不知道是什么导致工人永远阻塞,这是另一个需要解决的问题,但我需要有一种方法,至少在我们遇到这种情况时退出


我查看了ThreadPoolExecutor中的线程是如何创建的,它们被设置为
daemon=True
,因此我对这些线程为什么阻止应用程序退出感到双重困惑。

这是非常奇怪的预期行为。从
concurrent/futures/thread.py
(版本3.6.3):

“此问题”正是您希望在工作线程仍在运行时退出的行为。前面提到的退出处理程序在所有工作线程上调用了
join()
,如果它们被卡住,将永远阻塞:

def _python_exit():
    global _shutdown
    _shutdown = True
    items = list(_threads_queues.items())
    for t, q in items:
        q.put(None)
    for t, q in items:
        t.join()

atexit.register(_python_exit)
还有
TaskThreadExecutor
本身的
退出方法:

def __exit__(self, exc_type, exc_val, exc_tb):
    self.shutdown(wait=True)
    return False
self.shutdown
,使用
wait=True
,也会加入所有工作线程

要强制退出,我们需要覆盖这两个。如果按以下方式修改代码:

except concurrent.futures.TimeoutError:
    import atexit
    atexit.unregister(concurrent.futures.thread._python_exit))
    executor.shutdown = lambda wait:None
    raise TimeoutError()

然后您的脚本将根据需要退出。

(感谢您的编辑-不确定从何处开始)。感谢您的解决方案。我当时正在绞尽脑汁,不知道如何撤销他们的关机干预。那么,如果
daemon=True
不能实现主要目的,它有什么好处呢?呃,这很糟糕,因为
atexit.unregister
将禁用您甚至不知道的任何线程池执行器的定期关机。
except concurrent.futures.TimeoutError:
    import atexit
    atexit.unregister(concurrent.futures.thread._python_exit))
    executor.shutdown = lambda wait:None
    raise TimeoutError()