Python如何终止固定时间执行任务的scheduler调度器及对应线程?
实现思路与方案
方案1:改造现有sched实现(最小改动适配现有代码)
sched.scheduler本身自带cancel()方法可以删除待执行的任务,任务清空后调度器的run()方法会自动退出,对应的线程也会自然终止,不需要暴力杀死线程。你只需要把调度器实例、待执行的事件实例、线程实例提前存到类属性里,方便后续调用取消逻辑即可。
修改后的代码示例
import sched import time import threading import pytz # 假设这是你的类 class FlightMonitor: def __init__(self, player): self.player = player # 存储当前运行的调度器、调度事件、监控线程 self.current_scheduler = None self.current_event = None self.monitor_thread = None def stand_by(self, evt_time, number): """Waiting for scheduled time Starts a scheduler in a separate thread that will run in the background until a specific time is reached, usually a time in a flight's schedule. Once that specific time is reached, the program will regularly check if the state of the flight has changed as expected. Args: evt_time (:obj:`datetime`): The scheduled time of the status change. Schedulers need Unix timestamps, so this has to be converted inside the function. number (str): Flight number of the flight that should be observed. This function itself does not need it but it has to be passed on as an argument. """ # 先取消之前正在运行的任务(如果存在) self.cancel_existing_task() # evt_time is given in UTC time but needs to be checked against # system time self.current_scheduler = sched.scheduler(time.time, time.sleep) tz = pytz.timezone(self.player.tz) # Turns the event time from datetime UTC into datetime player actual evt_time = pytz.utc.localize(evt_time).astimezone(tz) # Turns the datetime object into a UNIX time stamp evt_time = evt_time.timestamp() # 存储调度事件,后续取消用 self.current_event = self.current_scheduler.enterabs( evt_time, 2, self.check_dep, [number]) # 启动线程 self.monitor_thread = threading.Thread(target=self.current_scheduler.run) self.monitor_thread.start() def cancel_existing_task(self): """取消当前正在等待的调度任务""" if self.current_scheduler and self.current_event: try: self.current_scheduler.cancel(self.current_event) except ValueError: # 事件已经执行或者已经被取消,忽略报错 pass # 等待线程退出(可选,非必须) if self.monitor_thread and self.monitor_thread.is_alive(): self.monitor_thread.join(timeout=1) # 重置属性 self.current_scheduler = None self.current_event = None self.monitor_thread = None def check_dep(self, number): # 你的航班检查逻辑 pass
当用户切换航班时,直接调用cancel_existing_task()方法即可终止之前的定时任务,再调用新的stand_by()传入新的航班参数即可。
方案2:使用threading.Timer(更简单,代码量更少)
threading.Timer本身自带cancel()方法,你只需要把目标datetime转成时间戳,减去当前时间戳得到间隔秒数,就能直接用Timer实现你的需求,不需要自己维护scheduler。
代码示例
import threading import time import pytz class FlightMonitor: def __init__(self, player): self.player = player self.current_timer = None def stand_by(self, evt_time, number): # 先取消旧任务 self.cancel_existing_task() tz = pytz.timezone(self.player.tz) # 转换时区得到目标时间戳 evt_time = pytz.utc.localize(evt_time).astimezone(tz).timestamp() # 计算距离当前的时间间隔 delay = max(0, evt_time - time.time()) # 启动Timer self.current_timer = threading.Timer(delay, self.check_dep, args=[number]) self.current_timer.start() def cancel_existing_task(self): if self.current_timer: self.current_timer.cancel() self.current_timer = None def check_dep(self, number): # 你的航班检查逻辑 pass
这个方案对初学者更友好,代码更简洁,不需要理解sched的调度逻辑,也能完全满足你的需求。
注意事项
- 如果目标时间已经过去,两种方案都会立刻执行检查逻辑,不会报错
- 取消操作不会影响已经开始执行的
check_dep逻辑,如果需要中途中断正在运行的检查逻辑,可以额外加一个threading.Event标志位做判断
内容的提问来源于stack exchange,提问作者Syrus Drake
相关产品推荐
相关产品推荐

