flock()
的PHP文檔頁面表明在IIS下使用它並不安全。如果我在任何情況下都不能依賴flock
,還有另一種方法可以安全地實現同樣的目標嗎?PHP flock()備選
回答
在所有想象的可能情況下,沒有其他方法可以安全地達到同樣的效果。這是計算機系統的設計和the job is not trivial for cross-platform code。
如果您需要安全使用flock()
,請改爲記錄您的應用程序的要求。
或者,您可以創建自己的鎖定機制,但是您必須確保它是原子性的。這意味着,您必須測試該鎖,如果該鎖不存在,則需要確保沒有其他東西可以獲取該鎖之間的鎖時建立該鎖。
這可以通過創建表示鎖的鎖文件來完成,但只有在它不存在的情況下才能完成。不幸的是,PHP沒有提供這樣的功能來創建文件。
或者,您可以創建一個包含mkdir()
的目錄並處理結果,因爲目錄創建時它將返回true
,如果它已經存在,將返回false
。
我的建議是使用mkdir()
而不是flock()
。這是讀/寫高速緩存顯示差異的真實的例子:
$data = false;
$cache_file = 'cache/first_last123.inc';
$lock_dir = 'cache/first_last123_lock';
// read data from cache if no writing process is running
if (!file_exists($lock_dir)) {
// we suppress error messages as the cache file exists in 99,999% of all requests
$data = @include $cache_file;
}
// cache file not found
if ($data === false) {
// get data from database
$data = mysqli_fetch_assoc(mysqli_query($link, "SELECT first, last FROM users WHERE id = 123"));
// write data to cache if no writing process is running (race condition safe)
// we suppress E_WARNING of mkdir() because it is possible in 0,001% of all requests that the dir already exists after calling file_exists()
if (!file_exists($lock_dir) && @mkdir($lock_dir)) {
file_put_contents($cache_file, '<?php return ' . var_export($data, true) . '; ?' . '>')) {
// remove lock
rmdir($lock_dir);
}
}
現在,我們試圖達到同樣與flock()
:
$data = false;
$cache_file = 'cache/first_last123.inc';
// we suppress error messages as the cache file exists in 99,999% of all requests
$fp = @fopen($cache_file, "r");
// read data from cache if no writing process is running
if ($fp !== false && flock($fp, LOCK_EX | LOCK_NB)) {
// we suppress error messages as the cache file exists in 99,999% of all requests
$data = @include $cache_file;
flock($fp, LOCK_UN);
}
// cache file not found
if (!is_array($data)) {
// get data from database
$data = mysqli_fetch_assoc(mysqli_query($link, "SELECT first, last FROM users WHERE id = 123"));
// write data to cache if no writing process is running (race condition safe)
$fp = fopen($cache_file, "c");
if (flock($fp, LOCK_EX | LOCK_NB)) {
ftruncate($fp, 0);
fwrite($fp, '<?php return ' . var_export($data, true) . '; ?' . '>');
flock($fp, LOCK_UN);
}
}
的重要組成部分,是LOCK_NB
,以避免阻塞所有連續請求:
也可以到LOCK_NB添加爲位掩碼上述 操作之一,如果你不希望flock()阻止WH ile鎖定。
沒有它,代碼會產生一個巨大的瓶頸!
另外一個重要部分是if (!is_array($data)) {
。這是因爲$數據可能包含:
array()
作爲數據庫查詢- 的失敗
include
- 或空字符串(race condition)
競爭狀態發生false
的結果如果第一位訪客執行此行:
$fp = fopen($cache_file, "c");
和另一個訪問者一毫秒後執行該行:
if ($fp !== false && flock($fp, LOCK_EX | LOCK_NB)) {
這意味着第一訪問者創建空文件,但第二訪問者創建鎖等include
返回一個空字符串。
$filename = 'index.html';
$loops = 10000;
$start = microtime(true);
for ($i = 0; $i < $loops; $i++) {
file_exists($filename);
}
echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;
$start = microtime(true);
for ($i = 0; $i < $loops; $i++) {
$fp = @fopen($filename, "r");
flock($fp, LOCK_EX | LOCK_NB);
}
echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;
結果:
file_exists: 0.00949
fopen/flock: 0.06401
附註:
所以,你通過使用mkdir()
和7倍速度更快,也看到了可避免很多陷阱正如你所看到的,我在mkdir()
之前使用了file_exists()
。這是因爲my tests(德語)單獨使用mkdir()會導致瓶頸。
您可以實現文件鎖 - 開鎖基於的mkdir在你的讀/寫操作模式,因爲這是原子彈,非常快。我已經強調過這一點,並且不像mgutt沒有發現瓶頸。儘管如此,你必須關心僵局,這可能是mgutt經歷的。當兩次鎖定嘗試彼此保持等待時,就會發生死鎖。它可以通過鎖定嘗試的隨機時間間隔來彌補。像這樣:
// call this always before reading or writing to your filepath in concurrent situations
function lockFile($filepath){
clearstatcache();
$lockname=$filepath.".lock";
// if the lock already exists, get its age:
[email protected]($lockname);
// attempt to lock, this is the really important atomic action:
while ([email protected]($lockname)){
if ($life)
if ((time()-$life)>120){
//release old locks
rmdir($lockname);
$life=false;
}
usleep(rand(50000,200000));//wait random time before trying again
}
}
然後在你的文件中的文件路徑工作,當你做,請致電:
function unlockFile($filepath){
$unlockname= $filepath.".lock";
return @rmdir($unlockname);
}
我選擇了刪除舊鎖,在最大PHP執行時間以及之後腳本在解鎖前退出。更好的方法是在腳本失敗時總是刪除鎖。這有一條幹淨的道路,但我已經忘記了。
我很欣賞這個問題已經過了幾年,但我有點感覺到,一個可行的示例/替換羣可能值得建設。我已經根據其他答案,但對於純粹希望替換羣體功能的人(而不是同時寫入文件(儘管這確實反映了PHP手冊中的羣集示例)),我相信以下內容就足夠了:
function my_flock ($path,$release = false){
if ($release){
@rmdir($path);
} else {
return !file_exists($path) && @mkdir($path);
}
}
- 1. 如何使用flock()
- 2. PHP Flock寫入打開文件
- 3. PHP flock()與compress.zlib://不起作用?
- 4. PHP flock()用Simplexml打開,讀寫
- 5. PHP flock() - 引擎蓋下有什麼?
- 6. PHP同時文件訪問/ flock()問題
- 7. 在crontab中使用flock()
- 8. php exec('cd executables /')備選
- 9. flock(1)未能釋放鎖
- 10. bash flock:爲什麼200?
- 11. 有可能flock用LOCK_EX返回false?
- 12. 使用flock的PHP問題 - 文件鎖定
- 13. PHP flock()非阻塞仍然阻塞爲什麼?
- 14. PHP - 計數器問題使用flock函數
- 15. PHP檢查文件是否用flock()鎖定?
- 16. PHP - 限制與flock的cron作業重疊()
- 17. 選取與PDO準備在PHP
- 18. php pdo準備插入選擇
- 19. erlang進程間鎖定機制(如flock)
- 20. Windows上的Perl的flock()在哪裏?
- 21. C警告:函數的隱式聲明'flock'僅適用於兩個flock()調用之一
- 22. PHP篩選器vs PHP htmlspecialchars vs sqli準備
- 23. JQuery備用備選方案
- 24. PHP postgres備份
- 25. flock:-c只需要一個命令參數
- 26. FREETEXTTABLE備選
- 27. SimpleCursorAdapter備選
- 28. JSpinner ActionListener備選
- 29. Field.SetValue備選
- 30. Java InetAddress.getByName.isReachable()備選
如果您需要避免讀取長度爲0的文件,'flock()'也很不方便。這是因爲'flock()'只能在文件創建後被調用,所以不可能創建一個新文件並以原子方式寫入。 – rustyx
此外,我讀了強制性文件鎖定在Linux上不推薦使用,所以flock確實不理想(也有一些工作要設置) – Antony