PHP flock()无法锁定

5
我在以下场景中无法正确使用flock(),希望你能帮我解决问题。
下面这段代码分别放入两个不同的PHP脚本中:一个是test1.php,另一个是test2.php。该代码的目的是创建一个文件,只有一个进程(正确使用flock()代码的进程)可以写入它。许多不同的PHP脚本会尝试获取对该文件的独占锁定,但任何时候只有一个脚本可以访问,并且当它们未能获取锁定时,所有其他脚本都应该优雅地失败。
测试方式非常简单。将“test1.php”和“test2.php”放置在服务器上的Web可访问目录中。然后利用Firefox等浏览器先执行第一个脚本,接着立即从不同的浏览器选项卡中执行第二个脚本。如果代码从两个不同的PHP脚本(如“test1.php”和“test2.php”)运行,则似乎可以工作。但是,当从相同的“test1.php”脚本或“test2.php”脚本运行代码两次时,第二个运行的脚本将不会立即返回失败。
我唯一能想到的原因是,flock()将所有具有相同文件名的PHP进程视为同一进程。如果是这种情况,那么当从两个不同的浏览器选项卡中运行“test1.php”或“test2.php”时,PHP将看到它们是同一进程,因此不会失败。但对我来说,PHP不应该像这样设计,所以我想知道是否有其他人能够为我解决这个问题。
提前感谢!
<?
$file = 'command.bat';

echo "Starting script...";
flush();

$handle = fopen($file, 'w+');
echo "Lets try locking...";
flush();

if(is_resource($handle)){
    echo "good resource...";
    flush();

    if(flock($handle, LOCK_EX | LOCK_NB) === TRUE){
        echo "Got lock!";
        flush();
        sleep(100);
        flock($fp, LOCK_UN);
    }else{
        echo "Failed to get lock!";
        flush();
    }
}else{
    echo "bad resource...";
    flush();
}

exit;

非常感谢您对以上内容的帮助!

谢谢, 丹尼尔


1
难道更简单的解释不是当你在两个标签页中运行相同的PHP文件时,速度太慢了,锁已经被释放了吗? - developerwjk
也许不是。我没有意识到在php中,sleep需要的是秒而不是毫秒。可能更多是浏览器缓存问题,因为页面仍然返回相同的文本,因为它被客户端缓存了。 - developerwjk
3个回答

7

我曾经遇到过相同的情况,并发现问题出在浏览器上。

当向相同的URL进行多次请求时,即使在不同的标签或窗口中进行,浏览器也足够“聪明”,会等待第一个请求完成,然后尝试运行接下来的请求。

因此,虽然看起来锁没有起作用,但实际上浏览器(包括Chrome和Firefox)正在等待第一个请求完成后再运行第二个请求。

您可以通过在Chrome和Firefox中分别打开相同的URL来验证这一点。像我这样做,您可能会发现锁确实按预期工作。


我也遇到过这个问题。你可以在同一个浏览器中使用多个选项卡,只需对第二个选项卡进行硬刷新(Mac:Cmd + Shift + R,或Win:Ctrl + Shift + R),它就会正常工作。 - Nick Johnson
不是浏览器在等待后续尝试,而很可能是Web服务器在排队。 - Luke Cousins

1

0
你的脚本是正确的。
实际上发生的是,fopen()会等待文件可以打开!
如果你运行第一个脚本并使用sleep(20);
然后运行第二个脚本并使用sleep(1);
你会注意到第二个脚本实际上会等待20秒直到文件解锁。
因此,除非发生某些关键错误,否则它永远不会显示"Failed to get lock"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接