关键读写关键的锁定问题

  在文件的读写的过程中会用到一个PHP函数flock()。

  为了确保操作的有效性和完整性,可以通过锁机制将并发状态转换成穿行状态。假设一个应用场景:在存在较大并发的情况下,通过fwrite向文件尾部多次有序的写入数据,不加锁的情况下会发生什么?多次有序的写入操作相当于一个事务,我们此时需要保证这个事务的完整性。

  函数flock():

语法

flock(file,lock,block)
参数 描述
file 必需。规定要锁定或释放的已打开的文件。
lock 必需。规定要使用哪种锁定类型。
block 可选。若设置为 1 或 true,则当进行锁定时阻挡其他进程。

 

 


说明

flock() 操作的 file 必须是一个已经打开的文件指针。

lock 参数可以是以下值之一:

  • 要取得共享锁定(读取的程序),将 lock 设为 LOCK_SH(PHP 4.0.1 以前的版本设置为 1)。
  • 要取得独占锁定(写入的程序),将 lock 设为 LOCK_EX(PHP 4.0.1 以前的版本中设置为 2)。
  • 要释放锁定(无论共享或独占),将 lock 设为 LOCK_UN(PHP 4.0.1 以前的版本中设置为 3)。
  • 如果不希望 flock() 在锁定时堵塞,则给 lock 加上 LOCK_NB(PHP 4.0.1 以前的版本中设置为 4)。

示例说明:

  建立两个文件a.php和b.php

<?php
//a.php

$file = "temp.txt";
$fp = fopen($file,"w");
if(flock($fp,LOCK_EX)){
    fwrite($fp,"abc\r\n");
    sleep(10);
    fwrite($fp,"123\r\n");
    flock($fp,LOCK_UN);
}
fclose($fp);
?>

<?php
//b.php
$file = "temp.txt";
$fp = fopen($file,"r");
if(flock($fp,LOCK_SH)){
    echo fread($fp,1024);
    flock($fp,LOCK_UN);
}else{
    echo "文件被独占锁锁住了。。。";
}
fclose($fp);
?>

  先运行a.php,然后马上运行b.php,会发现b.php要等a.php运行完成后(即写入大量数据完成后),b.php才显示a.php写入的数据。

  现在将b.php修改为:

<?php
$file = "temp.txt";
$fp = fopen($file,"r");
if(flock($fp,LOCK_SH|LOCK_NB)){
    echo fread($fp,1024);
    flock($fp,LOCK_UN);
}else{
    echo "文件被独占锁锁住了。。。";
}
fclose($fp);
?>

  此时进行同样的操作,会发现b.php不等a.php写完数据(运行完)就立即显示"文件被独占锁锁住了。。。"

  结论:作文件缓存时,选好相关锁,不然可能导致读取数据不完整,或重复写入数据。(file_get_contents的锁不清楚,如上操作会得到不完整的数据。)

  多次同时执行,虽然都写了100行,但是事务1和事务2的数据交错写入,这并不是我们想要的结果。我们要的是事务完整的执行。此时需要锁机制flock就保证了在第一个事务执行完后再执行第二个。当某一个事务执行完flock时,因为我们在这里添加的是LOCK_EX(独占锁定),所以所有对资源的操作都会被阻塞,只有当该事务执行完后,后面的事务才会执行。

  什么时候用LOCK_EX,什么时候用LOCK_SH呢?

  读的时候:如果不想出现dirty数据,那么最好使用LOCK_SH共享锁。可以考虑以下三种情况:

  1.如果读的时候没有加共享锁,那么其它程序要写的话都会立即些成功。如果正好读了一半,然后被其它程序给写了,那么读的后一半就有可能跟前一半对不上(前一半是修改前的,后一半是修改后的);

  2.如果读的时候加上了共享锁(因为只是读,没必要使用排他锁即独占锁),这个时候,其它程序开始写,这个写程序没有使用锁,那么写程序会直接修改这个文件,也会导致前面一样的问题;

  3.最理想的情况是,读的时候加共享锁LOCK_SH,写的时候也进行加独占锁LOCK_EX,这样写程序会等着读程序完成之后才进行操作,而不会出现贸然操作的情况。

  写的时候:如果多个写程序不加锁,同时对文件进行操作,那么最后的数据有可能一部分是a程序写的,一部分是b程序写的。所以最理想的情况还是如上第三点:读的时候加共享锁LOCK_SH,写的时候也进行加独占锁LOCK_EX。

你可能感兴趣的:(读写)