Php 函数内的群集调用始终';成功';,忽略以前的锁

Php 函数内的群集调用始终';成功';,忽略以前的锁,php,locking,Php,Locking,为了防止我编写的基于PHP的守护进程的多个实例同时运行,我编写了一个简单的函数来获取进程启动时的锁,并在守护进程开始时调用它。代码的简化版本如下所示: #!/usr/bin/php <?php function acquire_lock () { $file_handle = fopen('mylock.lock', 'w'); $got_lock_successfully = flock($file_handle, LOCK_EX);

为了防止我编写的基于PHP的守护进程的多个实例同时运行,我编写了一个简单的函数来获取进程启动时的锁,并在守护进程开始时调用它。代码的简化版本如下所示:

#!/usr/bin/php
<?php

    function acquire_lock () {
        $file_handle = fopen('mylock.lock', 'w');
        $got_lock_successfully = flock($file_handle, LOCK_EX);
        if (!$got_lock_successfully) {
            throw new Exception("Unexpected failure to acquire lock.");
        }
    }

    acquire_lock(); // Block until all other instances of the script are done...

    // ... then do some stuff, for example:
    for ($i=1; $i<=10; $i++) {
        echo "Sleeping... $i\n";
        sleep(1);
    }

?>
#/usr/bin/php
当我多次并行执行上面的脚本时,我希望看到的行为——因为在整个脚本期间锁从未被显式释放——是脚本的第二个实例将等待第一个实例完成,然后再通过
acquire\u lock()
调用。换句话说,如果我在两个并行终端中运行这个特定的脚本,我希望看到一个终端计数为10,而另一个等待,然后看到另一个计数为10

事实并非如此。相反,我看到两个脚本愉快地并行执行——第二个脚本不会阻塞并等待锁可用

如您所见,我正在检查
flock
中的返回值,该值为
true
,表示已成功获取(独占)锁。然而,这显然并没有阻止另一个进程在同一个文件上获取另一个“独占”锁


为什么以及如何解决此问题?

只需将从返回的文件指针资源存储在全局变量中。在问题中给出的示例中,
acquire\u lock()
返回时,
$file\u handle
将在超出范围时自动销毁,这将释放
flock
取出的锁

例如,下面是问题中脚本的修改版本,显示了所需的行为(请注意,唯一的更改是将
fopen
返回的文件句柄存储在全局文件中):

#/usr/bin/php
请注意,这似乎是PHP中的一个bug。
flock
文档中的变更日志指出,在版本5.3.2中:

文件的资源句柄关闭时的自动解锁已删除。解锁现在总是必须手动完成

但至少对于PHP5.5来说,这是错误的
flock
锁通过显式调用
fclose
和超出范围的资源句柄释放

我在2014年11月报告这是一个bug,如果问题得到解决,我可能会更新这个问答对。如果我在那之前被食人鱼吃掉,你可以自己检查虫子报告,看看这种行为是否得到了纠正:

#!/usr/bin/php
<?php

    function acquire_lock () {
        global $lock_handle;
        $lock_handle = fopen('mylock.lock', 'w');
        $got_lock_successfully = flock($lock_handle, LOCK_EX);
        if (!$got_lock_successfully) {
            throw new Exception("Unexpected failure to acquire lock.");
        }
    }

    acquire_lock(); // Block until all other instances of the script are done...

    // ... then do some stuff, for example:
    for ($i=1; $i<=10; $i++) {
        echo "Sleeping... $i\n";
        sleep(1);
    }

?>