schedule.run_pending()必须在循环中手动调用,因schedule仅登记任务而不自动触发;需while True配合time.sleep(1)轮询,且须捕获job异常、避免阻塞、自行实现持久化。

schedule.run_pending() 必须在循环里手动调用
很多人以为 schedule.every().hour.do(job) 设完就自动跑了,结果发现任务根本没执行——因为 schedule 不启动后台线程,也不接管事件循环。它只负责“登记任务”,真正触发得靠你主动轮询。
最简可行写法是加个 while True: 循环,每秒调一次 schedule.run_pending():
import schedule
import time
<p>def job():
print("任务执行中")</p><p>schedule.every(10).seconds.do(job)</p><p>while True:
schedule.run_pending()
time.sleep(1) # 避免空转吃满 CPU注意:time.sleep(1) 不能省,否则 CPU 占用会飙升;但也不能设太大(比如 sleep(60)),否则 10 秒级任务可能严重延迟。
立即学习“Python免费学习笔记(深入)”;
多个任务共用一个循环时,run_pending() 是线性执行的
schedule.run_pending() 会按注册顺序逐个检查、触发所有到期任务,且是同步阻塞的。如果某个 job() 执行耗时 5 秒,后面本该在第 3 秒触发的任务就得等它结束才能运行。
常见应对方式:
- 把耗时操作扔进线程:
schedule.every().minute.do(lambda: threading.Thread(target=long_job).start()) - 用
concurrent.futures.ThreadPoolExecutor统一管理,避免线程无限创建 - 对严格时效要求的任务(如监控心跳),别和重活混在一个调度器里
别指望 schedule 自带并发能力——它压根没这个设计。
job 函数返回值会被 schedule 忽略,但抛异常会中断调度
schedule 不关心你的 job() 返回什么,但一旦它抛出未捕获异常(比如 requests.exceptions.ConnectionError),整个 run_pending() 调用就会终止,后续任务当天再不会被执行。
必须显式兜底:
def job():
try:
requests.get("https://api.example.com/health")
except Exception as e:
print(f"任务失败:{e}") # 至少记个 log更稳妥的做法是在外层包装一层通用错误捕获:
def safe_job(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logging.error(f"任务 {func.__name__} 异常", exc_info=True)
return wrapper
<p>schedule.every().hour.do(safe_job(my_actual_job))schedule 不支持持久化,重启即丢失所有任务
和 cron 把规则写进系统文件不同,schedule 的任务全在内存里。进程一挂、服务一重启,所有 every().day.at("09:00").do(...) 全没了。
真要替代 cron,得自己补上这一环:
- 用 JSON 或数据库存任务配置(如执行时间、函数名、参数)
- 启动时重新注册,注意函数名需能通过
globals()或导入路径找回 - 避免直接序列化 lambda 或闭包函数——它们无法被可靠反序列化
这不是 schedule 的缺陷,而是它定位就是轻量内存调度器。想靠它完全取代 cron,持久化、故障恢复、分布式协调这些都得自己搭。


















