关于PHP并行文件读写

5

在一个网站上有一个文件。一个 PHP 脚本会对它进行以下修改:

$contents   = file_get_contents("MyFile");

// ** Modify $contents **

// Now rewrite:
$file       = fopen("MyFile","w+");
fwrite($file, $contents);
fclose($file);

修改非常简单。它获取文件的内容并添加几行,然后覆盖文件。
我知道PHP有一个函数可以将内容附加到文件而不是再次覆盖它。但是,我希望继续使用这种方法,因为我将来可能会更改修改算法(因此附加可能不足够)。
无论如何,我正在测试这个代码,进行了大约100次请求。每次调用脚本时,我都会向文件中添加一行:
第一次调用:
First!
第二次调用:
First! Second!
第三次调用:
First! Second! Third!
相当酷吧。但接下来:
第四次调用:
Fourth!
第五次调用:
Fourth! Fifth!
正如您所看到的,前三行就消失了。
我已经确定问题不是内容字符串修改算法(我已经单独测试过)。在读取或写入文件时出了点问题。
我认为很可能是读取文件内容时出了问题:如果$contents因某种奇怪的原因为空,则上述行为就有意义。
虽然我不是PHP专家,但也许我同时执行了100个调用引起了这个问题。如果有两个进程,其中一个正在写入文件,而另一个正在读取它,会发生什么?
对于这个问题,推荐的方法是什么?多个进程同时读取/写入同一个文件时,该如何管理文件修改?

如果这些请求不是顺序执行的,file_get_contents() 可能会在前一个调用的数据有效刷新到文件之前被调用。您可能需要使用 fflush()flock() 确保文件只被一个脚本处理一次。 - Havenard
并且要始终验证file_get_contents()返回的是字符串而不是 FALSE - Havenard
file_get_contents 有时可能会失败,建议使用 empty() 进行检查。我还建议您尝试使用 curl。 - user3079266
empty()并不意味着失败,文件可能确实为空。请检查它是否为is_string()!== FALSE - Havenard
3个回答

3

问题在于同时对同一文件进行并行访问,当一个实例在写入文件时,另一个实例在文件更新之前读取该文件。PHP幸运地拥有锁定文件的机制,因此在锁定被释放并且文件已经更新之前,没有人可以从中读取。

flock() 

可以使用,并且相关文档在这里


3
你需要做的是使用 flock() (文件锁)。
我认为发生的情况是您的脚本在先前的脚本仍在写入文件时抓取了该文件。由于文件仍在被写入,所以当 PHP 抓取它时,它不存在,因此 php 得到一个空字符串,一旦后续过程完成,它就会覆盖先前的文件。
解决方案是在文件被锁定时让脚本 usleep() 几毫秒,然后再次尝试。只需确保对脚本的尝试次数设置限制即可。
注意:
如果另一个 PHP 脚本或应用程序访问该文件,则不一定会使用/检查文件锁定。这是因为文件锁通常被视为可选项,因为在大多数情况下它们并不需要。

2

您需要创建一个锁,以便任何并发请求都必须等待其轮到。这可以使用flock()函数来完成。您将不得不使用fopen()而不是file_get_contents(),但这应该不是问题:

$file = 'file.txt';
$fh = fopen($file, 'r+');
if (flock($fh, LOCK_EX)) { // Get an exclusive lock
    $data = fread($fh, filesize($file)); // Get the contents of file
    // Do something with data here...
    ftruncate($fh, 0); // Empty the file
    fwrite($fh, $newData); // Write new data to file
    fclose($fh); // Close handle and release lock
} else {
    die('Unable to get a lock on file: '.$file);
}

1
好的例子。请注意,截至5.3.2版本,您需要在关闭文件之前调用flock($fh, LOCK_UN); - Klox

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