php的文件锁flock总结

  • 先从应用方面,之后会从操作系统方面深入理解

如果两个进程同时写一个文件,那么两个进程写的内容势必会乱掉,那么如何保证内容不乱呢,加锁。
PHP加锁的函数为flock,该函数有3个参数:

  1. 文件指针;
  2. 锁类型,LOCK_SH共享锁,LOCK_EX独占锁,LOCK_UN释放锁。加上LOCK_NB将不会阻塞,而是立即返回。比如LOCK_SH|LOCK_NB;
  3. wouldblock,是个引用变量,一般不会用到,可以传入一个变量,然后打印该变量看函数赋的是什么值;

LOCK_SH共享锁,用在读取程序上;
LOCK_EX独占锁,用在写入程序上;

读取文件

  1. 如果在lock1.php读取一个文件时(未加锁),lock2.php往里面写(没有加锁),那么lock1.php有可能读取脏数据,即读到lock2.php写入的数据;
  2. 在lock1.php读取一个文件时(加了LOCK_SH锁),lock2.php往里面写(没有加锁),那么lock1.php还是会读取脏数据,即读到lock2.php写入的数据;
  • lock1.php:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?php

    $f = fopen('test.txt', 'r');

    if ( flock($f, LOCK_SH) ) {
    while (!feof($f)) {
    $contents = fread($f, 1);
    echo $contents.chr(10);
    usleep(100000);
    }
    flock($f, LOCK_UN);
    } else {
    echo 'no';
    }
    fclose($f);
  • lock2.php:

    1
    2
    3
    4
    5
    6
    <?php

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

    3.如果在lock1.php读取一个文件时(加了LOCK_SH锁),lock2.php往里面写(加上LOCK_EX锁),那么lock2.php会阻塞直到lock1.php读取完毕后再写入

  • lock1.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php

    $f = fopen('test.txt', 'r');

    if (flock($f, LOCK_SH)) {
    while (!feof($f)) {
    $contents = fread($f, 2);
    echo $contents.chr(10);
    usleep(10000);
    }
    } else {
    echo 'no lock'.chr(10);
    }
    fclose($f);
  • lock2.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php

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

    if ( flock($f, LOCK_EX) ) {
    //if ( flock($f, LOCK_EX|LOCK_NB) ) { 不会阻塞,立即返回
    fwrite($f, 'ok');
    flock($f, LOCK_UN);
    } else {
    echo 'no';
    }
    fclose($f);

写入文件

  1. 如果lock1.php写一个文件时(未加锁),lock2.php读取该文件(未加锁),那么lock2.php会读到脏数据
  2. 如果lock1.php写一个文件时(加上LOCK_EX锁),lock2.php读取该文件(未加锁),那么lock2.php还是会读到脏数据
  3. 如果lock1.php写一个文件时(加上LOCK_EX锁), lock2.php读取该文件(加了LOCK_SH锁),那么lock2.php会等待lock1.php写完并且释放锁后读取。
  • lock1.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php

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

    if (flock($f, LOCK_EX)) {
    for ($i = 0; $i<1000; $i++) {
    fwrite($f, $i.chr(10));
    sleep(1);
    }
    }
    fclose($f);
  • lock2.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php

    $f = fopen('test.txt', 'r');

    if (flock($f, LOCK_SH|LOCK_NB)) {
    while (!feof($f)) {
    $re = fread($f, 2);
    echo $re.chr(10);
    usleep(1000);
    }
    } else {
    echo 'file locked'.chr(10);
    }

如果多个进程同时写文件,为了避免错乱,那么每个进程都需要用flock来尝试获取锁,用LOCK_EX排它锁。