ThreadPoolExecutor超时后如何退出脚本

时间:2018-01-19 22:09:32

标签: python-3.x python-multithreading

我试图在一些工作时间后彻底退出,因为线程被某些东西阻挡了。我正在使用这样的repo=$1 relBranch=$2 branchStart=$3 workDir=${repo}-closeout-`date +"%Y-%m-%d"` git clone .../${repo}.git ${workDir} pushd ${workDir} # update release branch git checkout ${relBranch}; git pull # create branch from branch start git checkout ${branchStart} -b squash/${relBranch} # Squash Merge branch history into single commit git clean -d -f -x . git merge --squash ${relBranch} # check that status reports only staged changes git commit -m "release/0.9 squash ${branchStart}..HEAD" squashedCommit=`git log --format="%H" -n 1` # Prepare master git checkout master; git pull # create staging branch for delta check git checkout -b deltacheck/${relBranch} git clean -d -f -x . # check release branch history delta git cherry-pick --no-commit ${squashedCommit} git status # Diff git diff popd

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 没问题,但脚本永远不会退出,因为它正在等待被阻止的线程最终完成。我还不知道是什么导致工人永远阻止,这是另一个需要解决的问题,但是当我们遇到这种情况时,我需要有办法至少退出。

我看了ThreadPoolExecutor中的线程是如何创建的,并且它们被设置为# cleanup所以我更加困惑为什么这些线程阻止应用程序退出。

1 个答案:

答案 0 :(得分:3)

奇怪的是,这是预期的行为。来自concurrent/futures/thread.py(版本3.6.3):

# To work around this problem, an exit handler is installed which tells the
# workers to exit when their work queues are empty and then waits until the
# threads finish.

"这个问题"正是您想要的行为 - 在工作线程仍在运行时退出。提到的退出处理程序在所有工作线程上调用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)

还有__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()

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