PHP自帶了文件鎖函數:
bool flock ( int $handle , int $operation [, int &$wouldblock ] )
$handle 是打開的文件指針;
$operation 可以是
“LOCK_SH”,共享鎖定;“LOCK_EX”,獨占鎖定;“LOCK_UN”,釋放鎖定;“LOCK_NB”,防止flock鎖定時堵塞。
這裡主要說說“LOCK_EX”和“LOCK_NB”。
比如我們有兩個文件,如下。
flocka.php
$file = 'temp.txt';
$fp = fopen($file,'a');
for($i = 0;$i < 5;$i++)
{
fwrite($fp, "11111111n");
sleep(1);
}
fclose($fp);
flockb.php
代碼如下 復制代碼 $file = 'temp.txt';先運行flocka.php,然後馬上運行flockb.php。
結果:
11111111
22222222
22222222
22222222
22222222
22222222
11111111
11111111
11111111
11111111
說明不加文件鎖時,兩個文件會同時對txt文件進行寫入操作。
下面修改一下兩個php文件的代碼。
flocka.php
flockb.php
代碼如下 復制代碼 $file = 'temp.txt';同樣先運行flocka.php,然後馬上運行flockb.php。
會發現在flocka.php運行結束前,flockb.php一直處於等待狀態,只有當flocka.php運行結束後,flockb.php才會繼續執行。
輸出結果:
11111111
11111111
11111111
11111111
11111111
22222222
22222222
22222222
22222222
22222222
另外,在執行flock時,文件鎖會自動釋放。
還有一種辦法
如下代碼簡單模擬了這種事務並發狀態: process1.php
我們需要先執行第一個事務,在processdata.txt文件中寫入這100行。
process2.php
代碼如下 復制代碼 <?php
$num = 100;
$filename = "processdata.txt";
$fp = fopen($filename, "a");
for ($i = 0; $i < $num; $i++) {
fwrite($fp, "process2: " . $i . "rn");
usleep(100000);
}
fclose($fp);
?>
第二個事務,繼續向processdata.txt文件中寫入100行。
第二個事務,繼續向processdata.txt文件中寫入100行。
多次同時執行,雖然都寫了100行,但是事務1和事務2的數據交錯寫入,這並不是我們想要的結果。我們要的是事務完整的執行,此時我們需要有個機制去保證在第一個事務執行完後再執行第二個。在PHP中,flock函數完成了這一使命。在事物1和事務2的循環前面都加上: flock($fp, LOCK_EX); 就能滿足我們的需求,將兩個事務串行。
當某一個事務執行完flock時,因為我們在這裡添加的是LOCK_EX(獨占鎖定),所以所有對資源的操作都會被阻塞,只有當事務執行完成後,後面的事務才會執行。我們可以通過輸出當前的時間的方法來確認這一點。
關於在尾部追加寫入,在unix系統的早期版本中存在一個並發寫入的問題,如果要在尾部追加,需要先lseek位置,再write。當多個進程同時操作時,會因為並發導致的覆蓋寫入的問題,即兩個進程同時獲取尾部的偏移後,先後執行write操作,後面的操作會將前面的操作覆蓋。這個問題在後面以添加打開時的O_APPEND操作而得到解決,它將查找和寫入操作變成了一個原子操作。
在PHP的fopen函數的實現中,如果我們使用a參數在文件的尾部追加內容,其調用open函數中oflag參數為 O_CREAT|O_APPEND,即我們使用追加操作不用擔心並發追加寫入的問題。
在PHP的session默認存儲實現中也用到了flock文件鎖,當session開始時就調用PS_READ_FUNC,且以O_CREAT | O_RDWR | O_BINARY 打開session數據文件,此時會調用flock加上寫鎖,如果此時有其它進程訪問此文件(即同一用戶再次發起對當前文件的請求),就會顯示頁面加載中,進程被阻塞了。加寫鎖其出發點是為了保證此次會話中對session的操作事務能完整的執行,防止其它進程的干擾,保證數據的一致性。如果一個頁面沒有session修改操作,可以盡早的調用session_write_close()釋放鎖。
文件鎖是針對文件的鎖,除了這種釋義,還可以理解為用文件作為鎖。在實際工作中,有時為確保單個進程的執行,我們會在程序執行前判斷文件是否存在,如果不存在則創建一個空文件,在進程結束後刪除這個空文件,如果存在,則不執行。