首页 > 解决方案 > 为什么在 PHP 中使用“锁定”文件而不是仅仅计算进程?

问题描述

我见过很多例子,其中使用“锁定”文件来跟踪 PHP 脚本当前是否正在运行。

例子:

  1. 脚本开始
  2. 检查“/tmp/lockfile”当前是否被锁定
  3. 如果已锁定,请退出。如果没有,锁定文件并继续

这样,如果一个长时间运行的脚本启动了两次,那么只有第一个实例会运行。这是伟大的。

但是,绕过它似乎是错误的方法。我们为什么不检查进程是否已经像这样运行呢?

if(exec("ps -C " . basename(__FILE__) . " --no-headers | wc -l") > 1){
  echo "Already running.";
  exit;
}

这种方法有任何潜在的缺陷吗?为什么我经常看到“锁定”文件解决方法?用我们正在寻找的名称来计算进程似乎更准确....

标签: phpcommand-line-interfacedaemon

解决方案


根据这里的评论和我自己的观察,我列出了两种方法的优缺点:

flock方法:

优点:

  • 跨操作系统更兼容
  • 不需要 bash 知识
  • 更常用的方法,很多例子
  • 即使有exec()残疾人也可以使用
  • 可以在单个文件中使用多个锁,以允许同一文件同时运行不同的“模式”

缺点:

  • 这不是确定的。如果您的锁定文件被外部进程/用户删除,您最终可能会得到多个进程。如果您将锁定文件保存在/tmp目录中,这是一种有效的可能性,因为该目录中的所有内容都应该是“临时的”
  • 在某些情况下,当一个进程意外死亡时,文件锁可以转移到一个不相关的进程(起初我不相信这一点,但我发现它发生在 200 多个基于 unix 的系统中的实例(虽然很少),在 3不同的操作系统)

exec("ps -C...")方法

优点:

  • 由于您实际上是在计算进程,因此无论文件锁定等状态如何,它都会每次都起作用。

缺点:

  • 仅适用于 linux
  • 需要启用“exec”
  • 如果您更改脚本的名称,可能会导致双重处理(并确保您的脚本名称未在代码中硬编码)
  • 假设您的脚本只有一个正在运行的“模式”

编辑:我最终使用了这个:

if (exec("pgrep -x " . $scriptName . " -u ". $currentUser . " | wc -l") > 1)
{
  echo $scriptName . " is already running.\n";
  exit;
}

...因为ps除了进程名称之外,不允许您过滤进程的所有者,并且我希望允许该脚本在其他用户运行它时多次运行。


编辑2:

...因此,在运行了几天之后,它也不完美。不知何故,该进程在同一用户下的同一台机器上启动了多次。我唯一的猜测是有一些问题(内存不足等)导致pgrep它什么都不返回,而它应该返回一些东西。

因此,这意味着该flock方法和计数过程方法都不是 100% 可靠的。您必须确定哪种方法更适合您的项目。

最终,我使用了另一种解决方案,将当前任务的 PID 存储在“锁定”文件中,实际上并没有被锁定。然后,当脚本启动时,检查锁定文件是否存在,如果存在,则获取内容(脚本上次启动时的 PID)然后,通过将/proc/#PID#/cmdline内容与名称进行比较来检查它是否仍在运行正在运行的脚本。


推荐阅读