2

我的问题是:即使写操作使用排他锁,读取期间不能使用共享锁会导致写入错误吗?

假设我想创建一个基于文件的计数器,如下所示:

//increment counter by 1
$fp = fopen($path, 'r+b');
if (flock($fp, LOCK_EX)) {

    //read
    fseek($fp, 0, SEEK_END);
    $size = ftell($fp);
    fseek($fp, 0, SEEK_SET);
    if ($size == 0) {
        $counter = 0;
    } else {
        $data = fread($fp, $size);
        $counter = intval($data);
    }

    //do something with data we just read
    $counter ++;

    //write
    fseek($fp, 0, SEEK_SET);
    ftruncate($fp, 0);
    fwrite($fp, $counter);
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);
} else {
    fclose($fp);
    throw new Exception("Lock failed");
}

现在我想在其他地方展示它:

echo intval(file_get_contents($path));

注意file_get_contents 使用共享锁。

该代码已被证明在页面负载过重的情况下会损坏数据,即计数器被重置几次回到 0。

我更改了要使用的代码,fopen现在LOCK_SH看起来还可以,但我无法确认这确实是问题的根源,因为我无法控制负载。使用多个 CLI PHP 实例本地执行上述代码表明代码甚至可以使用file_get_contents...

4

2 回答 2

0

ftell手册中的注释讨论了以附加模式打开的文件的不可预测行为。也许您可以尝试以只读方式打开文件以进行读取,或者只是读取文件,我不明白为什么您在代码中有一行可以明确地将计数器设置为 0。

echo intval(false); //0

EOF 处的 fread 返回 false,因此:

$fp = fopen($path, 'r+b');
if (flock($fp, LOCK_EX)) {

    //read
    $data = fread($fp, $size);
    $counter = intval($data);

    //do something with data we just read
    $counter ++;

    //write
    fseek($fp, 0, SEEK_SET);
    ftruncate($fp, 0);
    fwrite($fp, $counter);
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);
} else {
    fclose($fp);
    throw new Exception("Lock failed");
}
于 2013-02-23T14:43:10.133 回答
0

...终于,找到了罪魁祸首。我们的生产服务器已flock关闭功能,不幸的是我们无法更改它。我想我必须实现自定义锁定机制。

这是由于生产服务器具有 NFS 文件系统和 NFS 上的锁定文件在大多数情况下根本不起作用。

于 2013-02-23T15:39:55.277 回答