php檔案鎖類防止並發的例子

來源:互聯網
上載者:User

工作間隙寫了個檔案鎖的類,用於解決並發的一些問題。

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
/**
 * 用於解決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);
    }
}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.