schedule库适合开发验证但非生产级,需循环调用run_pending()并sleep(1);apscheduler支持持久化与重试,适合生产;threading.timer仅适用于单次延迟;linux cron需用绝对路径并重定向日志。

用 schedule 库写定时任务最省心
它不依赖系统 cron,纯 Python 实现,适合开发阶段快速验证、轻量服务或嵌入已有脚本。但别把它当生产级调度器用——没失败重试、没任务持久化、进程一挂就全停。
常见错误现象:schedule.run_pending() 没放在循环里,跑完就退出;或者用 time.sleep() 阻塞主线程却忘了加 time.sleep(1) 间隔,CPU 占满。
- 必须手动调用
schedule.run_pending(),且得在循环中定期执行 - 推荐搭配
time.sleep(1),避免空转消耗 CPU - 支持链式写法:
schedule.every().hour.do(job),但所有时间单位(hour、day、minutes)都区分单复数,写错如minute会静默失败 - 函数参数只能靠
job.func(*args, **kwargs)传,不能直接写do(job(arg))—— 那是立即执行
import schedule
import time
<p>def say_hello(name):
print(f"Hello, {name}!")</p><p>schedule.every(2).seconds.do(say_hello, name="Alice")
while True:
schedule.run_pending()
time.sleep(1)需要可靠调度就上 APScheduler
APScheduler 是真正能进生产环境的选择,支持内存、SQLAlchemy、Redis 多种作业存储,也内置了线程/进程/异步事件循环的执行器。
使用场景:Web 后端里定时拉取数据、清理缓存、发日报;或需要任务失败后自动重试、暂停恢复、查看运行历史。
立即学习“Python免费学习笔记(深入)”;
- 默认使用
BackgroundScheduler,启动后不阻塞主线程,但主程序退出时要显式调用.shutdown() -
trigger类型决定触发逻辑:CronTrigger支持类 crontab 表达式,IntervalTrigger按固定间隔,别混用参数 - 如果用
SQLAlchemyJobStore,注意数据库连接池配置,否则高并发下可能连不上 - 任务函数里抛出异常默认会中断调度器,得配
coalesce=True和max_instances防堆积
threading.Timer 只适合单次延迟执行
它不是“定时任务”,而是“延迟执行一次”。很多人误以为设个 60 秒重复创建 Timer 就能模拟周期任务,结果对象泄漏、时间漂移、无法统一管理。
错误现象:Timer(60, job).start() 执行完就没了;递归调用自己容易栈溢出;没做异常捕获导致整个线程崩掉。
- 只用于“N 秒后执行一次”,比如发验证码后 5 分钟失效
- 真要周期性执行,必须在
job结尾再 new 一个Timer,但得确保前一个已 cancel 或完成,否则并发失控 - 没有调度状态跟踪,没法查“这个任务还在不在跑”
- 和 asyncio 不兼容,别在
async def里混用threading.Timer
Linux 上用系统 cron 调 Python 脚本要注意环境
直接写 python3 /path/to/script.py 很容易失败——cron 默认 PATH 很窄,找不到 python3,也加载不了你 virtualenv 里的包。
典型错误:/usr/bin/env: ‘python’: No such file or directory,或导入第三方库时报 ModuleNotFoundError。
- 绝对路径优先:
/home/user/venv/bin/python3 /home/user/job.py - 在脚本开头加
#!/home/user/venv/bin/python3并chmod +x,然后 cron 里直接写路径 - 避免用
~,cron 不展开家目录,一律写完整路径 - 输出重定向很重要:
*/5 * * * * /path/to/script.py >> /var/log/job.log 2>&1,不然失败了根本看不到错在哪
事情说清了就结束。定时任务看着简单,真正卡住人的永远是环境隔离、异常生命周期、以及“以为它在跑其实早就挂了”这种事。







