使用APScheduler BackgroundScheduler调用shutdown(wait=False)无法正常退出问题
APScheduler v3.6.3
shutdown(wait=False)不生效问题解决方案 问题现象
使用python v3.6.6、apscheduler v3.6.3版本的BackgroundScheduler搭配持久化存储,期望关闭调度器时无需等待运行中任务执行完成,添加wait=False参数后main函数依然无法正常退出,复现代码如下:
from datetime import datetime import time import os import logging from apscheduler.schedulers.background import BackgroundScheduler def tick(): print('Tick! The time is: %s' % datetime.now()) time.sleep(1000) if __name__ == '__main__': scheduler = BackgroundScheduler() scheduler.add_job(tick, 'interval', seconds=3) logging.basicConfig() logging.getLogger('apscheduler').setLevel(logging.DEBUG) scheduler.start() print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C')) try: # 模拟业务逻辑保活主线程 while True: time.sleep(2) except (KeyboardInterrupt, SystemExit): # 按官方建议调用关闭逻辑 scheduler.shutdown(wait=False) print("END.", flush=True)
日志显示调度器已触发关闭,但线程池工作线程仍存活,Python退出阶段触发atexit的线程join逻辑导致阻塞。
原因说明
wait=False参数的使用方式没有问题,该现象是APScheduler 3.x版本的默认设计导致:
shutdown(wait=False)仅作用于调度器本身,代表调度器不等待运行中任务完成就结束自身调度逻辑,不会主动终止执行器的工作线程- APScheduler 3.x默认使用的
ThreadPoolExecutor工作线程为非守护线程,Python进程退出时会自动等待所有非守护线程执行完成后再退出,上述示例任务中存在1000秒的sleep逻辑,因此会一直阻塞
修复方案
两种方案可解决该问题,按需选择即可:
方案1:将执行器工作线程设置为守护线程
初始化BackgroundScheduler时,自定义线程池执行器,将线程设置为守护属性,进程退出时不会等待守护线程完成:
# 新增导入 from concurrent.futures import ThreadPoolExecutor if __name__ == '__main__': # 初始化时指定自定义执行器,开启线程守护 scheduler = BackgroundScheduler( executors={ 'default': ThreadPoolExecutor(max_workers=10, thread_name_prefix='apscheduler_', daemon=True) } ) # 后续逻辑保持不变
方案2:调度器关闭后强制退出进程
如果不需要执行其他收尾逻辑,可在调用shutdown后直接调用os._exit(0)强制终止进程,跳过atexit的线程join逻辑:
except (KeyboardInterrupt, SystemExit): scheduler.shutdown(wait=False) os._exit(0)
内容的提问来源于stack exchange,提问作者neo_coder
相关产品推荐
相关产品推荐

