读取和写入LOCK_EX应该是原子的吗?

Kam*_*šík 21 php locking atomic blocking atomicity

file_put_contents ( "file", "data", LOCK_EX )
Run Code Online (Sandbox Code Playgroud)

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

file_get_contents ( "file", LOCK_EX )
Run Code Online (Sandbox Code Playgroud)

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

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

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

irc*_*ell 42

由于这个答案很长,这里是摘要: 不,file_get_contents()不是原子的,因为它不尊重咨询锁.

关于PHP中的文件锁:

在PHP中,在*nix平台上,文件系统锁定仅供参考.根据文档(强调我的):

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

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

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

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

阅读文件锁定以获取更多信息.即Unix下的部分:

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

因此,总之,using 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);
Run Code Online (Sandbox Code Playgroud)

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

$f = fopen('test.txt', 'a+');
fwrite($f, 'foobar');
fclose($f);
Run Code Online (Sandbox Code Playgroud)

输出将是foobar......

关于file_get_contents具体:

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

现在,查看源代码,我们可以看到file_get_contents在第521行定义的函数.没有调用内部函数,php_stream_lock因为当你传递file_put_contents('file', 'txt', LOCK_EX);同一文件的第589行定义时.

那么,让我们测试一下,我们应该:

在file1.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);
Run Code Online (Sandbox Code Playgroud)

在file2.php中:

var_dump(file_get_contents('test.txt'));
Run Code Online (Sandbox Code Playgroud)

运行时,file2.php立即返回.所以不,它似乎根本不file_get_contents尊重文件锁...


Pow*_*ord 5

程序员的理论问题比这里的要好得多。

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

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

话虽如此,flock默认情况下会阻塞,直到锁被释放。不过,请记住 ircmaxell 关于 Linux/BSD 上的建议锁的说明。

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

注 2:上一条说明有效,因为只有在不存在排他锁的情况下才能获取共享锁,但排他锁要求在获取锁之前不存在任何类型的锁。