我的问题是:在读取期间不使用共享锁是否会导致写入错误,即使写入操作使用独占锁?
假设我想创建一个基于文件的计数器,如下所示:
//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
,目前看来还可以,但我无法确认这确实是问题的根源,因为我无法控制负载。使用多个CLIPHP实例在本地执行上述代码表明,代码甚至可以使用file_get_contents
。。。
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");
}
。。。终于找到了罪魁祸首。我们的生产服务器已关闭flock
功能,不幸的是,我们无法更改此功能。我想我将不得不实现自定义锁定机制。
这是由于生产服务器具有NFS文件系统,并且在大多数情况下NFS上的锁定文件根本无法工作。