threading.timer 适合粗略延迟任务,如 ui 按钮禁用 2 秒;实际延迟受 gil 和系统调度影响,误差常达几十毫秒;不可重用,cancel() 后不能 restart();回调异常被静默吞掉。

用 threading.Timer 做简单延迟任务,但别指望它精准
它适合“大概等个几秒后执行一次”的场景,比如 UI 禁用按钮 2 秒、发个提醒日志。底层靠线程 + time.sleep(),所以实际延迟受 GIL 和系统调度影响,误差常达几十毫秒甚至更多。
- 不能取消后重用:创建后调用
start()一次,cancel()只是让定时器失效,不能restart() - 回调函数抛异常会被吞掉,主线程完全感知不到——得在回调里自己加
try/except - 大量短间隔定时器(如
示例:t = threading.Timer(3.0, lambda: print("done")); t.start()
asyncio.create_task() + asyncio.sleep() 是 asyncio 生态的正解
如果你整个程序已是异步架构(比如 FastAPI、aiohttp),这是最轻量、最可控的方式。没有额外线程开销,延迟精度取决于事件循环调度,通常比 threading.Timer 更稳。
- 必须在 running 的 event loop 中调用,直接脚本里写会报
RuntimeError: no running event loop - 延迟时间是“至少等待”,不是“精确在那一刻触发”;如果当前任务阻塞了事件循环(比如调用了同步 IO 或死循环),延迟就会被拖长
- 任务对象可保存,用
task.cancel()主动终止,且会抛CancelledError,方便清理资源
示例:asyncio.create_task(asyncio.sleep(2.5) or print("done"))(注意 or 是 trick,真实场景建议封装成独立协程)
立即学习“Python免费学习笔记(深入)”;
MALL的中文含义是购物中心,是区别于专卖店和百货公司的一个流行的商业模式,MALL里面是各个独立商家,自由自主的定价,各自管理自己的供销渠道和客户关系。电子商务的MALL模式其实就是对B2C业务模式做了多主体的扩展和延伸。目前具有代表性的电子商务MALL模式就是淘宝商城。比如淘宝电器城,他们的模式更像是做房地产的,阿里巴巴有着繁华的互联网商业物业,只是开了一个名字叫淘宝电器城的大市场而已,没有任
需要重复、持久或跨进程延迟?绕不开 APScheduler
当你要“每天上午 9 点发邮件”“每 30 秒检查一次 Redis 键是否存在”“服务重启后继续执行未完成的延时任务”,threading.Timer 和原生 asyncio 都撑不住。
- 默认使用
BackgroundScheduler,后台线程运行,不阻塞主线程;但要注意它和 asyncio 不兼容,混用会出错 - 若要用异步任务,得选
AsyncIOScheduler,且所有 job 函数必须是async def,否则报TypeError: a coroutine was expected - 持久化靠
SQLAlchemyJobStore或RedisJobStore,否则进程一挂,所有待执行任务全丢
常见错误:Scheduler started without any jobs——忘了调 add_job() 就 start() 了
别碰 signal.alarm() 和 os.setitimer()
它们只能绑定一个信号处理器,且只在主线程有效;Python 的 signal 处理本身不安全(比如不能在 handler 里做 print、log、IO),多线程下行为不可预测。Linux 上还可能被子进程继承导致意外中断。
- Windows 完全不支持
signal.alarm() -
setitimer()的ITIMER_REAL虽然能跨线程,但 Python 没提供安全的回调机制,容易引发InterruptedError或静默失败 - 现代项目里几乎找不到合理使用场景,除非你在写 C 扩展或嵌入式胶水脚本
真要底层控制,优先考虑 asyncio.loop.call_later() 或交由消息队列(如 Celery + Redis Broker)处理








