You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何避免sleep函数异常?Cron定时脚本运行间隔异常求助

问题分析与解决方案

我来帮你搞定这个头疼的问题!首先得搞明白核心症结:Cron只负责两个脚本的第一次启动间隔,后续什么时候运行完全看脚本自己的循环逻辑。你看到第二次运行几乎同时启动,肯定是两个脚本的循环没维持住初始的5分钟偏移,导致运行时间慢慢“撞”到一块儿了。

为啥会出现这种情况?

  • 两个脚本都是「做完任务就睡5分钟」,如果任务执行时间长短不一,比如一个脚本跑10秒,另一个跑4分50秒,那它们下一次启动的时间差就会从5分钟变成20秒,慢慢就缩到几秒了。
  • 要是脚本里加了“强制整分运行”的逻辑(比如不管啥时候都等整分再执行),那两个脚本最终都会卡在同一个时间点触发,自然就重叠了。

具体怎么解决?

1. 给循环加固定偏移,死死维持5分钟差

让其中一个脚本永远比另一个晚5分钟运行,从首次启动到后续循环都保持这个节奏:

脚本1(Cron设置成 */5 * * * * /path/to/script1.sh,每5分钟触发一次):

#!/bin/sh
LOGFILE="/var/log/script1.log"

# 先检查有没有同名脚本在跑,避免Cron重复启动
if pgrep -f "$0" | grep -v $$ > /dev/null; then
    echo "$(date): 脚本已经在运行啦,这次就跳过啦" >> "$LOGFILE"
    exit 0
fi

while true; do
    # 这里放你的任务逻辑
    echo "$(date): 脚本1开始干活儿啦" >> "$LOGFILE"
    sleep 10  # 模拟任务运行时间,替换成你的实际任务

    # 干完睡5分钟,下次再跑
    sleep 300
done

脚本2(Cron设置成 5-59/5 * * * * /path/to/script2.sh,比脚本1晚5分钟触发):

#!/bin/sh
LOGFILE="/var/log/script2.log"

# 同样加个进程检查,防止重复启动
if pgrep -f "$0" | grep -v $$ > /dev/null; then
    echo "$(date): 脚本正在运行,本次跳过" >> "$LOGFILE"
    exit 0
fi

while true; do
    echo "$(date): 脚本2开始干活儿啦" >> "$LOGFILE"
    # 你的任务逻辑
    sleep 10

    # 维持5分钟间隔
    sleep 300
done

这样设置后,脚本2第一次启动就比脚本1晚5分钟,后续每一轮循环也都是5分钟一次,永远不会撞车。

2. 加个互斥锁,兜底防冲突

要是担心极端情况(比如脚本意外卡了导致时间偏移),可以给两个脚本加个共用的锁文件,确保同一时间只有一个在跑:

在两个脚本的开头都加上这段代码:

LOCKFILE="/tmp/script_mutex.lock"

# 尝试拿锁,10秒拿不到就放弃
if ! flock -x -w 10 "$LOCKFILE"; then
    echo "$(date): 另一个脚本正在忙,这次先不跑啦" >> "$LOGFILE"
    exit 1
fi

# 脚本退出时自动释放锁,避免死锁
trap 'flock -u "$LOCKFILE"' EXIT INT TERM

这个方案是个“安全垫”,就算时间逻辑出了问题,也不会让两个脚本同时执行,彻底避免冲突。

3. 让脚本严格对齐时间点运行

如果想让脚本精准卡在指定分钟运行(比如脚本1在0、5、10分,脚本2在5、10、15分),可以用下面的方式计算sleep时长,确保每次都对齐目标时间:

脚本1的循环部分:

while true; do
    # 执行你的任务
    echo "$(date): 脚本1运行任务" >> "$LOGFILE"
    sleep 10  # 替换成你的实际任务

    # 算一下离下一个5分钟整还有多少秒
    NEXT_RUN=$(( (5 - $(date +%M) % 5) * 60 - $(date +%S) ))
    # 要是已经过了目标时间,就等下一个周期
    if [ "$NEXT_RUN" -le 0 ]; then
        NEXT_RUN=$(( NEXT_RUN + 300 ))
    fi
    sleep "$NEXT_RUN"
done

脚本2的循环部分:

while true; do
    # 执行你的任务
    echo "$(date): 脚本2运行任务" >> "$LOGFILE"
    sleep 10  # 替换成你的实际任务

    # 算一下离下一个(5的倍数+5)分钟整还有多少秒(比如5分、10分、15分)
    CURRENT_MIN=$(date +%M)
    NEXT_MIN=$(( (CURRENT_MIN / 5 + 1) * 5 ))
    NEXT_RUN=$(( (NEXT_MIN - CURRENT_MIN) * 60 - $(date +%S) ))
    if [ "$NEXT_RUN" -le 0 ]; then
        NEXT_RUN=$(( NEXT_RUN + 300 ))
    fi
    sleep "$NEXT_RUN"
done

这种方式不管任务跑多久,下一次都会精准对齐到目标时间点,牢牢维持5分钟的间隔。


内容的提问来源于stack exchange,提问作者Raymond Danner

火山引擎 最新活动