wing的个人作品集

定时任务为什么执行了两次

2026-09-12 · RedEarth 开发笔记

RedEarth 每天早上会自动生成一份资讯摘要。有一天我发现同一份摘要收到了两遍,时间完全一样。

第一反应:是不是开了两个进程

最常见的原因是 Web 服务开了多个 worker,每个 worker 都启动了一个调度器。但我检查以后发现服务只有一个进程,这个猜测排除了。

在调度器启动处打日志

我在创建调度器的地方加了一行日志,打印进程号和对象的 id:

logger.info("scheduler start pid=%s id=%s", os.getpid(), id(scheduler))

重启后日志里出现了两行,进程号相同,对象 id 不同。也就是说,同一个进程里创建了两个调度器。

原因:同一个文件被当成两个模块导入

启动入口是 app.py,它在模块顶层直接调用了 create_app()。服务以脚本方式运行时,这个文件的模块名是 __main__;而项目里另有一处代码写了:

from app import app

Python 发现 sys.modules 里没有名为 app 的模块,于是把 app.py 又执行了一遍,create_app() 被调用了第二次,调度器也就启动了第二个。

这是 Python 里一个经典的坑:一个文件既作为脚本运行,又被其他代码按模块名导入,就会被执行两次。

修复

根治的方法是在入口文件里,把 __main__ 模块登记为 app 的别名,这样后面的导入会直接复用已加载的模块:

import sys
if __name__ == "__main__":
    sys.modules.setdefault("app", sys.modules["__main__"])

同时我又加了一层兜底:定时任务执行前先在 Redis 里抢一把短期锁,key 里带上任务名和计划执行时间,抢不到就跳过。这样即使将来因为其他原因又出现了两个调度器,任务也只会执行一次。

收获