如何使用 ctrl+c 立即杀死 apscheduler.BlockingScheduler?

问题描述 投票:0回答:1

背景: 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秒杀呢?

python apscheduler
1个回答
0
投票

您需要在演示代码中添加一些内容。首先我们必须定义一个

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()
© www.soinside.com 2019 - 2024. All rights reserved.