应该LOCK_EX读取&写原子?

时间:2011-02-04 15:33:20

标签: php locking blocking atomic atomicity

file_put_contents ( "file", "data", LOCK_EX )

用于写入(意思是 - 获取锁定和写入)

file_get_contents ( "file", LOCK_EX )

用于阅读(这意味着 - 获取锁定然后阅读)

它会抛出异常吗?提出错误?阻止直到获得锁定? 或者至少 - 应该?有一天,php会有这样的表现吗?

编辑:我知道可以使用重命名 - 我想知道答案...

2 个答案:

答案 0 :(得分:41)

由于答案很长,以下是摘要:不,file_get_contents()不是原子的,因为它不尊重顾问锁

关于PHP中的文件锁:

在PHP中,在* nix平台上,文件系统锁定仅供参考。每the docs(强调我的):

  

PHP支持以咨询方式锁定完整文件的可移植方式(这意味着所有访问程序必须使用相同的锁定方式或不起作用)。默认情况下,此功能将阻止,直到获取所请求的锁定为止;这可以通过下面记录的LOCK_NB选项进行控制(在非Windows平台上)。

因此,只要访问该文件的所有进程都使用这种锁定方法,你就可以了。

但是,如果您使用理智的Web服务器编写静态HTML文件,则会忽略锁定。在写入过程中,如果有请求进入,Apache将提供部分写入的文件。锁定对读取锁定的其他进程没有影响。

唯一真正的例外是,如果在文件系统上使用-o mand的特殊挂载选项来启用强制锁定(但实际使用不多,并且可能会降低性能)。

请阅读File Locking以获取更多信息。即 Unix 下的部分:

  

这意味着合作进程可以使用锁来协调对文件之间的访问,但是程序也可以自由地忽略锁并以他们选择的任何方式访问文件。

因此,总而言之,使用LOCK_EX将在文件上创建一个咨询锁。只有在读者尊重和/或检查锁定时,才会阻止任何读取文件的尝试。如果他们不这样做,锁将被忽略(因为它可以)。

试一试。在一个过程中:

file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);

在它睡觉的时候,请打电话给:

$f = fopen('test.txt', 'a+');
fwrite($f, 'foobar');
fclose($f);

输出为foobar ...

具体关于file_get_contents

对于您的其他具体问题,首先,file_get_contents没有LOCK_EX参数。所以你无法通过它。

现在,查看source code,我们可以看到第521行定义的函数file_get_contents。没有调用内部函数php_stream_lock,就像通过{{{ 1}}在同一文件的第589行定义。

所以,让我们测试一下,我们是否应该:

在file1.php中:

file_put_contents('file', 'txt', LOCK_EX);

在file2.php中:

file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);

运行时,var_dump(file_get_contents('test.txt')); 会立即返回。所以不,file2.php看起来根本不尊重文件锁......

答案 1 :(得分:3)

理论问题在Programmers上的效果要好于此。

此时,PHP不支持原子文件锁定。

简单地说,PHP不支持组合fopenflock操作,因此在您的进程之前,另一个进程锁定您的进程也已打开的文件总会有一个小机会窗口。进程可以锁定它。

话虽如此,默认情况下,flock将阻止,直到锁定被释放。请记住ircmaxell关于Linux / BSD上的咨询锁的说明。

注意:对于读取过程,您可能希望将其设为LOCK_SH而不是LOCK_EX,以便多个读取器线程可以同时锁定它。写作必须始终使用LOCK_EX或风险数据损坏。

注意2:之前的注释有效,因为如果不存在排它锁,则只能获取共享锁,但是排它锁要求在获取锁之前不存在任何类型的锁。