2026-10-06 · 喵窝资源铺
场景:定时任务跑得比上一轮慢——爬虫卡住、网络重试、大文件处理——下一轮 cron 又启动了,两个实例同时写同一个文件/同一个队列,轻则数据错乱,重则互相覆盖。网上常见答案是「脚本里用 PID 文件判断」,但 PID 文件方案要处理进程死了没删文件、误杀别人 PID 等一堆边界。Linux 自带的 flock 是更省事的解法:内核级文件锁,进程死了锁自动释放,零残留。一行改完 crontab:
*/10 * * * * flock -n /tmp/myjob.lock /path/to/your_script.sh
拿到不了锁就静默退出(-n = nonblocking),上一轮还在跑就跳过本轮。这行几乎不需要解释,下面是实跑验证的细节和两个易踩的坑。
方法:父进程用 Python fcntl 持锁,子进程用 flock 命令行尝试拿同一把锁。
# 父进程持锁时
$ python3 -c "import fcntl,subprocess; f=open('/tmp/t.lock','w'); fcntl.flock(f,fcntl.LOCK_EX|fcntl.LOCK_NB); print(subprocess.run(['flock','-n','/tmp/t.lock','-c','echo inside'],capture_output=True).returncode)"
1 # 子进程拿不到锁,rc=1,'inside' 没有输出
# 释放后再试
... fcntl.flock(f,fcntl.LOCK_UN) ...
0 # 子进程拿到锁,输出 'inside'
结论:fcntl(代码)与 flock(命令行)锁的是同一把内核锁,混用没问题——比如主脚本是 Python 用 fcntl,包装层是 shell 用 flock,互斥依然生效。
Python 的fcntl.flock是按打开的文件描述(open file description)加锁的,不是按文件路径。同一个进程里open()同一个文件两次、分别 flock,两把锁互不冲突(都能拿到)。也就是说 fcntl 方案只对「不同进程」互斥——这正好是 cron 场景的需求,但如果你的脚本内部开了多线程又各自 open 同一个文件,fcntl 挡不住线程之间的并发,得换threading.Lock。
flock -n ... cmd拿不到锁时退出码是 1,cron 的邮件通知/监控可能把它当失败告警。如果这个任务是「跑不到就算了」型,直接忽略;如果需要区分「真的崩了」和「被锁跳过了」,用退出码 255 通道:flock -n -E 0 /tmp/myjob.lock cmd(-E 指定拿不到锁时的退出码,0 表示视为正常)。
工作目录可能被同步盘/备份工具扫描,或随部署被整个删掉重建,锁文件一并消失。放/tmp或/var/lock,路径固定、权限干净,也不用写清理逻辑(flock 锁在进程退出时自动释放,文件残留无害,下次直接复用)。
-n 改成 flock /tmp/myjob.lock cmd,默认阻塞等待定时任务、批处理脚本、自动化运维这类活,跑起来之后的坑(锁、重试、日志轮转)比写代码本身多。喵窝资源铺提供脚本代写+调试服务,交付前在本机实跑验证。
👉 脚本代写服务(¥19.9 起) | 更多实测工具清单
本文所有代码与命令均在 Linux 实机验证,输出为真实运行结果。