核心是用 flock -xn 锁文件实现单实例控制:抢不到独占锁则立即退出,不启动新实例;一行命令接入、内核级原子性、异常退出自动释放锁,比手动 touch/pid 检查更安全可靠。

用 flock 防止 crontab 任务重复执行,核心是让每次调度尝试先抢一把“独占锁”,抢不到就直接退出,不启动新实例。它不依赖脚本内部逻辑,也不需要改代码,一行命令就能接入,且由内核保证原子性,比手动建文件、查进程安全得多。
flock 基本用法:加锁再执行
把原本的 crontab 条目:
* */2 * * * /usr/bin/python3 /opt/task.py
改成:
* */2 * * * /usr/bin/flock -xn /tmp/task.lock -c "/usr/bin/python3 /opt/task.py"
关键参数说明:
- -x:申请独占锁(默认值,可省略),确保同一时间只有一个实例能拿到锁
- -n:非阻塞模式——拿不到锁立刻失败,不等待,避免卡住调度
- /tmp/task.lock:锁文件路径,建议用绝对路径;flock 会自动创建(如果不存在)
- -c:后面跟一个带引号的完整 shell 命令,必须加引号,否则空格和重定向会出错
为什么推荐 flock 而不是自己 touch + pid 检查
手动用 touch lockfile 再检查是否存在,看似简单,但有明显缺陷:
- 进程崩溃或被 kill -9 时,lockfile 不会被自动清理,后续所有任务都因“锁存在”而跳过,变成死锁
- 多个进程同时执行
test ! -e lock && touch lock可能发生竞态,两个进程都判断为“无锁”然后都创建成功 - flock 的锁由内核管理,文件描述符关闭即自动释放,哪怕脚本异常退出也不会残留锁
实际使用注意事项
几个容易踩坑的点:
- 锁文件路径尽量避开 tmpfs(如
/tmp)以外的挂载点,尤其不要放在 NFS 或某些容器临时卷上——flock 在这些文件系统上可能不生效 - 不同任务必须用不同锁文件,比如
/tmp/backup.lock和/tmp/sync.lock不能混用 - 日志重定向要写在
-c引号内部,例如:flock -xn /tmp/job.lock -c "python script.py >> /var/log/job.log 2>&1" - 若脚本本身会 fork 子进程(如调用后台服务),加
-o参数更稳妥:flock -xno /tmp/job.lock -c "python script.py",防止子进程继承锁句柄导致提前释放
替代方案适用场景
flock 是单机场景下的首选,但有些情况它不够用:
- 脚本运行在多个 Docker 容器里,或 Kubernetes 多 Pod 中 → 单机锁失效,必须用 Redis 分布式锁(带唯一 value + Lua 安全解锁)
- 无法修改 crontab(如受运维权限限制),只能改脚本 → 可在 Python 开头用
os.open(..., os.O_CREAT | os.O_EXCL)做原子文件锁 - 任务本身耗时波动大、触发密集(如每分钟)→ 除了加锁,还应错开执行时间,比如改用
0,15,30,45 * * * *或开头加sleep $((RANDOM % 60))


















