多進程/執行緒同時寫入同一檔案會導致資料損毀。PHP flock() 提供諮詢式檔案鎖,配合阻塞等待機制,可安全實現併發寫入。
/**
* 安全寫入檔案(獨佔鎖 + 超時重試)
* @param string $file_name 檔案路徑
* @param string $text 寫入內容
* @param string $mode fopen 模式,預設 'a' 追加
* @param int $timeout 最大等待秒數
* @return bool
*/
function file_write($file_name, $text, $mode='a', $timeout=30) {
$handle = fopen($file_name, $mode);
if (!$handle) return false;
// 等待取得獨佔鎖
while ($timeout > 0) {
if (flock($handle, LOCK_EX)) break; // 成功取得鎖
$timeout--;
sleep(1);
}
if ($timeout > 0) {
fwrite($handle, $text . "\n");
flock($handle, LOCK_UN); // 釋放鎖
fclose($handle);
return true;
}
fclose($handle);
return false; // 超時未取得鎖
}
| 常數 | 值(舊版) | 用途 |
|---|---|---|
| LOCK_SH | 1 | 共享鎖(讀取) |
| LOCK_EX | 2 | 獨佔鎖(寫入) |
| LOCK_UN | 3 | 釋放鎖 |
| LOCK_NB | 4 | 非阻塞模式(配合 LOCK_EX|LOCK_NB) |
flock($handle, LOCK_UN) 後再 fclose()'a' 追加寫入(指標自動移至末尾)、'w' 截斷重寫、'r+' 讀寫不截斷fcntl 或分散式鎖function file_read_locked($file_name) {
$handle = fopen($file_name, 'r');
if (!$handle) return false;
if (flock($handle, LOCK_SH)) {
$content = stream_get_contents($handle);
flock($handle, LOCK_UN);
fclose($handle);
return $content;
}
fclose($handle);
return false;
}
if (flock($handle, LOCK_EX | LOCK_NB)) {
// 立即獲得鎖,執行寫入
flock($handle, LOCK_UN);
} else {
// 未獲得鎖,可選擇排隊、丟棄或記錄日誌
}
檔案鎖適合低併發、單機場景(如日誌寫入、簡易計數器)。高併發或分散式環境請改用 Redis SETNX、資料庫行鎖、或專用協調服務。
(ZooKeeper、etcd)