crontab 任务在上一轮还没跑完时又被触发,就会造成重复处理:重复扣款、重复发短信、重复跑结算。PHP 没有内置的进程互斥,但用 flock 文件锁可以简单可靠地解决。

实现思路

脚本启动后尝试对锁文件加 LOCK_EX 排它锁并配合 LOCK_NB 非阻塞:加锁成功说明没有其它实例在跑,正常执行;加锁失败说明已有实例占用,直接退出。锁文件按脚本路径 MD5 命名,避免互相干扰。

完整代码

<?php
/**
 * flock 防止脚本重复执行
 * 用法:把需要互斥的任务逻辑放在加锁成功之后即可
 */
$lockFile = sys_get_temp_dir() . "/job_" . md5(__FILE__) . ".lock";
$fp = fopen($lockFile, "w");
if (!$fp) {
    exit("无法创建锁文件");
}

// LOCK_EX 排它 + LOCK_NB 非阻塞
if (!flock($fp, LOCK_EX | LOCK_NB)) {
    fclose($fp);
    exit("已有实例运行中,本次跳过");
}

echo "任务开始:" . date("Y-m-d H:i:s") . PHP_EOL;
sleep(5);          // 模拟耗时任务

flock($fp, LOCK_UN);
fclose($fp);
echo "任务完成" . PHP_EOL;
?>

使用注意

  • 锁是劝告性锁,必须所有执行同一任务的脚本都走同一把锁才有效;
  • 进程异常退出时操作系统会自动释放文件锁,不会出现死锁残留;
  • 多个定时任务请各用各的锁文件名,避免互相误伤。

小结

文件锁实现简单、跨平台可用,适合单机任务调度;分布式多机部署时再迁移到 Redis SETNX 或任务调度中间件。