flock() 在某些系統(特別是 Windows 舊版 PHP)上不可靠或不支援。本文提供一種以標記檔案模擬獨佔鎖的思路,不依賴系統底層鎖 API。
.{$lock}0(申請鎖)、.{$lock}1(持有鎖)usleep 避免忙等待佔用 CPU<?php
/**
* 申請檔案鎖(模擬 flock)
* @param string $lock 基礎檔名(如 "data.txt")
* @param int $tries 最大重試次數
* @return int 1=成功 0=超時失敗
*/
function lock($lock, $tries) {
$lock0 = ".{$lock}0"; // 申請標記
$lock1 = ".{$lock}1"; // 持有標記
for ($i = 0; $i < $tries; $i++) {
if (!is_file($lock0)) {
touch($lock0); // 建立申請標記
if (!is_file($lock1)) {
touch($lock1); // 建立持有標記
return 1; // 成功取得鎖
}
}
usleep(100); // 讓出 CPU,避免忙等待
}
return 0; // 超時失敗
}
/**
* 釋放檔案鎖
*/
function unlock($lock) {
unlink(".{$lock}1");
unlink(".{$lock}0");
}
/* ===== 使用範例 ===== */
$filename = "somefile";
$data = "stuff and things\n";
$tries = 10;
if (lock($filename, $tries)) {
$h = fopen($filename, "a") or die();
fwrite($h, $data);
fclose($h);
/* 模擬另一程序嘗試寫入(會被鎖住) */
if (lock($filename, $tries)) {
$h2 = fopen($filename, "a") or die();
fwrite($h2, 'check lock');
fclose($h2);
} else {
// echo "第二程序無法取得鎖";
}
unlock($filename);
} else {
// die("首次鎖定失敗");
}
?>
| 優點 | 缺點 |
|---|---|
| 純 PHP 實作,跨平台相容 | 依賴檔案系統,NFS/網路磁碟可能有延遲導致競態 |
| 無需系統級 flock 支援 | 進程異常終止可能殘留標記檔(需定期清理) |
| 邏輯簡單易維護 | 效能不如核心級 flock,高併發場景建議用 Redis/Memcached 分散式鎖 |
register_shutdown_function('unlock', $lock) 防止腳本異常結束殘留鎖flock()(現代 PHP 已完善支援)或分散式鎖