背景: Python 3.8 AP调度器 3.10.1
我正在使用 BlockingScheduler 进行一些 cron 作业,理想情况下我希望可以使用 ctrl+c 轻松停止调度程序进程,因此我按如下方式安排我的代码,但到目前为止它没有按我的预期正常工作。
这是调度程序基类:
import traceback
from typing import Any, Callable
from apscheduler.schedulers.background import BlockingScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from utils.tool.decorator import sched_job
from utils.tool.logger import log
logger = log(__file__, "utils", warning_only=False)
class APSchedulerBase:
def __init__(self, scheduler: Any = None, add_listener: bool = True):
if scheduler is None:
self.sched = BlockingScheduler(timezone="Asia/Shanghai")
if add_listener:
self.sched.add_listener(self.ap_listener, EVENT_JOB_ERROR | EVENT_JOB_EXECUTED)
else:
self.sched = scheduler
def ap_listener(self, event):
if event.exception == KeyboardInterrupt or event.exception == SystemExit:
self.on_kill()
if event.exception:
logger.error(str(traceback.format_exc()))
else:
pass
def on_kill(self):
logger.warning("Scheduler Exit.")
self.sched.shutdown(wait=False)
def register_task(
self, func: Callable, trigger: str,
**kwargs
):
@sched_job(
self.sched,
logger,
trigger=trigger,
**kwargs
)
def wrap():
func()
wrap()
def start(self):
try:
logger.warning("Scheduler Start.")
self.sched.start()
except (KeyboardInterrupt, SystemExit):
self.on_kill()
这是一个简单的演示: ctrl+c 仅在 f() 即将被调用时起作用。
import time
def f():
print(1)
time.sleep(10)
print(2)
s = APSchedulerBase()
s.register_task(f, trigger='cron', hour=12, minute=30)
s.start()
现在整个调度程序进程只能在任务即将执行时被杀死,如果您在进程休眠/等待下一个任务时按ctrl+c,则在触发下一个任务之前不会发生任何事情。 那么具体应该怎么做才能让ctrl+c秒杀呢?
您需要在演示代码中添加一些内容。首先我们必须定义一个
signal_handler
函数:
def signal_handler(scheduler, signal_number, frame):
# here you can add logging
scheduler.shutdown(wait=False)
然后在文件顶部导入
signal
:
import signal
最后在创建调度程序之前:
signal.signal(signal.SIGINT, self.signal_handler)
为什么需要这个?
当我们按下 CTRL+C 时,一个 SIGNINT 被发送到 foreground 进程。请检查这个。那么,在我们的例子中,谁在运行调度程序的线程或我们创建调度程序的线程中获得此信号?答案是后者。因此,由于没有任何处理程序,因此什么也不会发生。另一方面,调度程序进程只能在任务即将执行时被杀死,因为调度程序正在运行的线程中引发异常,并且有一个
except
语句捕获此异常并关闭调度程序
except (KeyboardInterrupt, SystemExit):
self.on_kill()