php文件锁类防止并发的例子

本文讲的是php文件锁类防止并发的例子, 工作间隙写了个文件锁的类,用于解决并发的一些问题。bool flock ( int handle, int operation [, int &wouldblock] );flock() 操作的 handle 必须是一

工作间隙写了个文件锁的类,用于解决并发的一些问题。

bool flock ( int handle, int operation [, int &wouldblock] );
flock() 操作的 handle 必须是一个已经打开的文件指针。operation 可以是以下值之一:

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

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

$file = "temp.txt";   
$fp = fopen($file , 'r');   
echo fread($fp , 100);   
fclose($fp);  
运行 a.php 后,马上运行 b.php ,可以看到输出:
abc
等 a.php 运行完后运行 b.php ,可以看到输出:
abc
123
显然,当 a.php 写文件时数据太大,导致时间比较长时,这时 b.php 读取数据不完整

修改 b.php 为:

$file = "temp.txt";   
$fp = fopen($file , 'r');   
if(flock($fp , LOCK_EX)){   
    echo fread($fp , 100);   
    flock($fp , LOCK_UN);   
} else{   
    echo "Lock file failed...\n";   
}   
fclose($fp);  
运行 a.php 后,马上运行 b.php ,可以发现 b.php 会等到 a.php 运行完成后(即 10 秒后)才显示:
abc
123
读取数据完整,但时间过长,他要等待写锁释放。

修改 b.php 为:

$file = "temp.txt";   
$fp = fopen($file , 'r');   
if(flock($fp , LOCK_SH | LOCK_NB)){   
    echo fread($fp , 100);   
    flock($fp , LOCK_UN);   
} else{   
    echo "Lock file failed...\n";   
}   
fclose($fp);  
运行 a.php 后,马上运行 b.php ,可以看到输出:
Lock file failed…
证明可以返回锁文件失败状态,而不是向上面一样要等很久。

结论:
建议作文件缓存时,选好相关的锁,不然可能导致读取数据不完整,或重复写入数据。
file_get_contents 好像选择不了锁,不知道他默认用的什么锁,反正和不锁得到的输出一样,是不完整的数据。
我是要做文件缓存,所以只需要知道是否有写锁存在即可,有的话就查数据库就可以了。
测试环境:Linux(Ubuntu 6) , PHP 5.1.2 , Apache 2

再转:

文件锁有两种:共享锁和排他锁,也就是读锁(LOCK_SH)和写锁(LOCK_EX)
文件的锁一般这么使用:
$fp = fopen("filename", "a");  
flock($fp, LOCK_SH) or die("lock error")  
$str = fread($fp, 1024);  
flock($fp, LOCK_UN);  
fclose($fp); 

注意fwrite之后,文件立即就被更新了,而不是等fwrite然后fclose之后文件才会更新,这个可以通过在fwrite之后fclose之前读取这个文件进行检查

但是什么时候使用lock_ex什么时候使用lock_sh呢?

读的时候:
如果不想出现dirty数据,那么最好使用lock_sh共享锁。可以考虑以下三种情况:
1. 如果读的时候没有加共享锁,那么其他程序要写的话(不管这个写是加锁还是不加锁)都会立即写成功。如果正好读了一半,然后被其他程序给写了,那么读的后一半就有可能跟前一半对不上(前一半是修改前的,后一半是修改后的)
2. 如果读的时候加上了共享锁(因为只是读,没有必要使用排他锁),这个时候,其他程序开始写,这个写程序没有使用锁,那么写程序会直接修改这个文件,也会导致前面一样的问题
3. 最理想的情况是,读的时候加锁(lock_sh),写的时候也进行加锁(lock_ex),这样写程序会等着读程序完成之后才进行操作,而不会出现贸然操作的情况

写的时候:
如果多个写程序不加锁同时对文件进行操作,那么最后的数据有可能一部分是a程序写的,一部分是b程序写的
如果写的时候加锁了,这个时候有其他的程序来读,那么他会读到什么东西呢?
1. 如果读程序没有申请共享锁,那么他会读到dirty的数据。比如写程序要写a,b,c三部分,写完a,这时候读读到的是a,继续写b,这时候读读到的是ab,然后写c,这时候读到的是abc.
2. 如果读程序在之前申请了共享锁,那么读程序会等写程序将abc写完并释放锁之后才进行读。


/**
 * 用于解决PHP在并发时候的锁控制,不同的锁之间并行执行,类似mysql innodb的行级锁
 */
class FileLock{
    //文件锁存放路径
    private $path='';
    //文件句柄
    private $fp='';
    //锁文件
    private $lockFile='';
    /**
     * 构造函数
     * @param string $path 锁的存放目录
     * @param string $name 锁 KEY
     */
    public function __construct($name,$path=''){
  if(empty($path)) $this->path=dirname(__FILE__).'/';
  else $this->path=$path;
        $this->lockFile=$this->path.md5($name).'.lock';
    }
 
    /**
     * 加锁
     */
    public function lock(){
  $this->fp=fopen($this->lockFile,'a+');
  if($this->fp===false){
   return false;
  }
  return flock($this->fp,LOCK_EX);//获取独占锁
    }
    
    /**
     * 解锁
     */
    public function unlock(){
  if($this->fp!==false){
   @flock($this->fp,LOCK_UN);
   clearstatcache();
  }
  @fclose($this->fp);
  @unlink($this->lockFile);
    }
}

以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索php , 文件 , 数据 程序 php 文件锁 高并发、php 文件锁 并发、php 高并发防止重复、php 防止并发、php 防止并发提交,以便于您获取更多的相关知识。


flock
(PHP 4, PHP 5)

flock — 轻便的咨询文件锁定

说明
bool flock ( int $handle , int $operation [, int &$wouldblock ] )
PHP 支持以咨询方式(也就是说所有访问程序必须使用同一方式锁定, 否则它不会工作)锁定全部文件的一种轻便方法。

Note:

在 Windows 下 flock() 将会强制执行。

flock() 操作的 handle 必须是一个已经打开的文件指针。operation 可以是以下值之一:


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

flock() 允许执行一个简单的可以在任何平台中使用的读取/写入模型(包括大部分的 Unix 派生版和甚至是 Windows)。如果锁定会堵塞的话(EWOULDBLOCK 错误码情况下),可选的第三个参数会被设置为 TRUE。锁定操作也可以被 fclose() 释放(代码执行完毕时也会自动调用)。

成功时返回 TRUE, 或者在失败时返回 FALSE.


Example #1 flock() 例子

复制代码 代码如下:

$fp = fopen("/tmp/lock.txt", "w+");
if (flock($fp, LOCK_EX)) { // 进行排它型锁定
fwrite($fp, "Write something here ");
flock($fp, LOCK_UN); // 释放锁定
} else {
echo "Couldn't lock the file !";
}
fclose($fp);
?>

Note:

由于 flock() 需要一个文件指针, 因此可能不得不用一个特殊的锁定文件来保护打算通过写模式打开的文件的访问(在 fopen() 函数中加入 "w" 或 "w+")。

Warning
flock() 不能用于 NFS 以及其它一些网络文件系统。详细资料查看自己操作系统的文档。
在部分操作系统中 flock() 以进程级实现。当用一个多线程服务器 API(比如 ISAPI)时,可能不可以依靠 flock() 来保护文件,因为运行于同一服务器实例中其它并行线程的 PHP 脚本可以对该文件进行处理。
flock() 不支持旧的文件系统,如 FAT 以及它的派生系统。因此,此环境下总是返回 FALSE(尤其是对 Windows 98 用户来说)。

php中文件锁函数flock函数用法简介:

语法:

bool flock ( int $handle , int $operation [, int &$wouldblock ] )
flock() 操作的 handle 必须是一个已经打开的文件指针。operation 可以是以下值之一:
1. 要取得共享锁定(读取程序),将 operation 设为 LOCK_SH(PHP 4.0.1 以前的版本设置为 1)
2. 要取得独占锁定(写入程序),将 operation 设为 LOCK_EX(PHP 4.0.1 以前的版本中设置为 2)
3. 要释放锁定(无论共享或独占),将 operation 设为 LOCK_UN(PHP 4.0.1 以前的版本中设置为 3)
4. 如果你不希望 flock() 在锁定时堵塞,则给 operation 加上 LOCK_NB(PHP 4.0.1 以前的版本中设置为 4)

看下面代码:

a.php
复制代码 代码如下:

$file = “temp.txt”;
$fp = fopen($file , 'w‘);
if(flock($fp , LOCK_EX)){
fwrite($fp , “abc”);
sleep(10);
fwrite($fp , “123”);
flock($fp , LOCK_UN);
}
fclose($fp);
?>

b.php
复制代码 代码如下:

$file = “temp.txt”;
$fp = fopen($file , 'r');
echo fread($fp , 100);
fclose($fp);
?>

运行 a.php 后,马上运行 b.php ,可以看到输出:
abc
等 a.php 运行完后运行 b.php ,可以看到输出:
abc
123
显然,当 a.php 写文件时数据太大,导致时间比较长时,这时 b.php 读取数据不完整,在对b.php做修改
修改 b.php 为:
复制代码 代码如下:

$file = “temp.txt”;
$fp = fopen($file , 'r‘);
if(flock($fp , LOCK_EX)){
echo fread($fp , 100);
flock($fp , LOCK_UN);
} else{
echo “Lock file failed…”;
}
fclose($fp);
?>

运行 a.php 后,马上运行 b.php ,可以发现 b.php 会等到 a.php 运行完成后(即 10 秒后)才显示:
abc
123
读取数据完整,但时间过长,他要等待写锁释放,再对b.php做修改。
修改 b.php 为:
复制代码 代码如下:

$file = “temp.txt”;
$fp = fopen($file , 'r');
if(flock($fp , LOCK_SH | LOCK_NB)){
echo fread($fp , 100);
flock($fp , LOCK_UN);
} else{
echo “Lock file failed…”;
}
fclose($fp);
?>

运行 a.php 后,马上运行 b.php ,可以看到输出:
Lock file failed…
证明可以返回锁文件失败状态,而不是向上面一样要等很久。
脚本之家小编结论:
建议作文件缓存时,选好相关的锁,不然可能导致读取数据不完整,或重复写入数据。
file_get_contents 好像选择不了锁,不知道他默认用的什么锁,反正和不锁得到的输出一样,是不完整的数据。


文件锁定
//文件锁定分四种类型
//1.LOCK_SH(取得共享锁定) 从文件中读取数据时使用
//2.LOCK_EX(取得独占锁定) 向文件中写入数据时使用
//3.LOCK_UN(释放锁定) 无论共享或独占锁,都用它释放
//4.LOCK_NB(附加锁定) 如果不希望flock()在锁定时堵塞,则应在上述锁定后加上改锁

///
// 

向文件内写文件
//  @param $filename
//  @param $message
//
function writeMessage($filename,$message){
    $fp =fopen($filename,"a");
   if(flock($fp,LOCK_EX)){
      fwrite($fp,$message);
      flock($fp,LOCK_UN);
    }else{
       echo'不能锁定文件';
    }
   
   fclose($fp);
}

///
//  读取文件内容
//  @param unknown_type $filename
//
function readMessage($filename){
    $fp =fopen($filename,"r");
   flock($fp,LOCK_SH);
   
   while(!feof($fp)){
       $buffer .=fread($fp,1024);
    }
   flock($fp,LOCK_UN);
   fclose($fp);
}

// 如果不希望出现锁定堵塞发生,可以附加LOCK_NB,
$file = fopen("test.txt","w+"); // 以读写的方式打开文件
flock($file,LOCK_EX+LOCK_NB);  // 独占锁定加上附加锁定

fwrite($file,"write somemessage"); // 向文件中写放数据
flock($file,LOCK_UN+LOCK_NB);       //释放锁定也加上了附加锁定

fclose($file);
?>

php flock 使用实例


[php] view plain copy
  1. bool flock ( resource $handle , int $operation [, int &$wouldblock ] )  

flock()允许执行一个简单的可以在任何平台中使用的读取/写入模型(包括大部分的Unix派生版和windows)

在PHP 5.3.2版本之前,锁会被 fclose() 释放(在脚本结束后会自动调用),现在需要手动解锁


operation

1.LOCK_SH 取得共享锁(读取的程序)

2.LOCK_EX 取得独占锁(写入的程序)

3.LOCK_UN 释放锁定 (无论共享或独占)

4.LOCK_NB 在flock()锁定时不阻塞


实例1:A使用独占锁写文件,B读取文件,阻塞

a.php

[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'w');  
  4.   
  5. if(flock($fp, LOCK_EX)){            // 取得独占锁  
  6.     fwrite($fp"Hello World\r\n"); // 写入数据  
  7.     sleep(10);                      // sleep 10秒,文件被锁定  
  8.     fwrite($fp"Hello PHP\r\n");   // 写入数据  
  9.     flock($fp, LOCK_UN);            // 解锁  
  10. }  
  11.   
  12. fclose($fp);  
  13. ?>  

b.php
[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'r');  
  4.   
  5. if(flock($fp, LOCK_SH)){ // 取得贡献锁  
  6.     while(!feof($fp)){  
  7.         echo fread($fp, 100);  
  8.     }  
  9.     flock($fp, LOCK_UN);  
  10. }  
  11.   
  12. fclose($fp);  
  13. ?>  

先执行a.php,然后执行b.php

a取得独占锁,b只能等待,等a执行完解除锁定后才能执行b,阻塞


实例2:A,B都使用共享锁,不阻塞

a.php

[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'r');  
  4.   
  5. if(flock($fp, LOCK_SH)){ // 取得共享锁  
  6.     sleep(10);           // sleep 10秒  
  7.     while(!feof($fp)){  
  8.         echo fread($fp, 100);  
  9.     }  
  10.     flock($fp, LOCK_UN);  
  11. }  
  12.   
  13. fclose($fp);  
  14. ?>  

b.php
[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'r');  
  4.   
  5. if(flock($fp, LOCK_SH)){ // 取得共享锁  
  6.     while(!feof($fp)){  
  7.         echo fread($fp, 100);  
  8.     }  
  9.     flock($fp, LOCK_UN);  
  10. }  
  11.   
  12. fclose($fp);  
  13. ?>  

先执行a.php,然后执行b.php

b不需要等待a执行完就能输出文件内容,非阻塞


实例3:A,B都使用独占锁写文件,阻塞

a.php

[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'a');  
  4.   
  5. if(flock($fp, LOCK_EX)){            // 取得独占锁  
  6.     fwrite($fp"Hello World\r\n"); // 写入数据  
  7.     sleep(10);                      // sleep 10秒,文件被锁定  
  8.     fwrite($fp"Hello PHP\r\n");   // 写入数据  
  9.     flock($fp, LOCK_UN);            // 解锁  
  10. }  
  11.   
  12. fclose($fp);  
  13. ?>  

b.php
[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'a');  
  4.   
  5. if(flock($fp, LOCK_EX)){                    // 取得独占锁  
  6.     fwrite($fp"How Are You\r\n");         // 写入数据  
  7.     fwrite($fp"Show Me The Money\r\n");   // 写入数据  
  8.     flock($fp, LOCK_UN);                    // 解锁  
  9. }  
  10.   
  11. fclose($fp);  
  12. ?>  

先执行a.php,然后执行b.php

b需要等待a执行完,才能写入数据,阻塞


实例4:LOCK_NB 锁定时不阻塞,不等待

a.php

[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'a');  
  4.   
  5. if(flock($fp, LOCK_EX)){            // 取得独占锁  
  6.     fwrite($fp"Hello World\r\n"); // 写入数据  
  7.     sleep(10);                      // sleep 10秒,文件被锁定  
  8.     fwrite($fp"Hello PHP\r\n");   // 写入数据  
  9.     flock($fp, LOCK_UN);            // 解锁  
  10. }  
  11.   
  12. fclose($fp);  
  13. ?>  

b.php
[php] view plain copy
  1. $file = 'test.txt';  
  2.   
  3. $fp = fopen($file'a');  
  4.   
  5. if(flock($fp, LOCK_EX|LOCK_NB)){            // 取得独占锁  
  6.     fwrite($fp"How Are You\r\n");         // 写入数据  
  7.     fwrite($fp"Show Me The Money\r\n");   // 写入数据  
  8.     flock($fp, LOCK_UN);                    // 解锁  
  9. }else{  
  10.     echo 'file locked';  
  11. }  
  12.   
  13. fclose($fp);  
  14. ?>  

先执行a.php,然后执行b.php

b取不到独占锁,不需要等待a执行完,而是直接返回取不到锁提示,非阻塞


总结:

使用共享锁LOCK_SH,如果是读取,不需要等待,但如果是写入,需要等待读取完成。

使用独占锁LOCK_EX,无论写入/读取都需要等待。

LOCK_UN,无论使用共享/读占锁,使用完后需要解锁。

LOCK_NB,当被锁定时,不阻塞,而是提示锁定。


你可能感兴趣的:(php文件锁类防止并发的例子)