在高併發場景下,讀取文件內容並清空(如日誌輪轉、臨時數據交換)需注意原子性與數據一致性。本文提供一個使用 flock() 獨佔鎖、ftruncate() 截斷、rewind() 重置指針的標準流程,確保讀取與清空操作不可分割。
<?php
$path = '/tmp/data.json';
$new_content = ['new' => 'data']; // 要追加/覆寫的新數據
$fh = fopen($path, 'r+'); // 以讀寫模式打開,文件必須存在
if ($fh === false) {
die('無法打開文件');
}
if (flock($fh, LOCK_EX)) { // 獲取獨佔鎖(阻塞直到獲得)
// 1. 讀取原內容
$old_content = fread($fh, filesize($path));
$old_data = json_decode($old_content, true) ?? [];
// 2. 業務邏輯:合併新數據(示例為數組合併)
$merged = array_merge($old_data, $new_content);
// 3. 截斷文件至長度 0,並將指針移回開頭
ftruncate($fh, 0);
rewind($fh);
// 4. 寫入新內容
fwrite($fh, json_encode($merged, JSON_UNESCAPED_UNICODE));
// 5. 釋放鎖
flock($fh, LOCK_UN);
} else {
// 非阻塞模式下獲取失敗的處理
error_log('獲取文件鎖失敗: ' . $path);
}
fclose($fh);
| 函數/操作 | 作用 | 注意事項 |
|---|---|---|
fopen($path, 'r+') | 以讀寫模式打開,指針在文件開頭,文件必須存在 | 若文件可能不存在,先用 'a+' 創建再切換,或預先創建空文件 |
flock($fh, LOCK_EX) | 獨佔鎖(寫鎖),阻塞其他進程讀寫 | 配合 LOCK_NB 可非阻塞嘗試;Windows 下 flock 為強制鎖,Linux 為建議性鎖,需所有進程配合使用 |
fread($fh, filesize($path)) | 一次性讀取全部內容 | 大文件建議分塊讀取,避免內存溢出 |
ftruncate($fh, 0) | 將文件大小截斷為 0 字節 | 指針位置不變,必須配合 rewind() |
rewind($fh) | 將文件指針移回開頭(偏移量 0) | 等同於 fseek($fh, 0, SEEK_SET) |
fwrite($fh, $data) | 寫入新數據 | 返回寫入字節數,建議檢查是否完整寫入 |
flock($fh, LOCK_UN) | 釋放鎖 | 務必在 fclose 前釋放,否則可能導致死鎖 |
'w+' 會直接截斷文件,導致讀不到舊數據;必須用 'r+' 或 'a+'(需先 rewind)。filesize() 受緩存影響,且大文件讀取會佔用大量內存,改用流式處理(fgets/fread 循環)或臨時文件交換。flock 可能不可靠,改用 link() 原子創建鎖文件或 Redis 分布式鎖。function read_write_atomic(string $path, callable $processor): bool {
$fh = fopen($path, 'r+');
if (!$fh) return false;
if (!flock($fh, LOCK_EX)) {
fclose($fh);
return false;
}
try {
$content = fread($fh, filesize($path));
$new_content = $processor($content); // 用戶自定義處理邏輯,返回新字符串
ftruncate($fh, 0);
rewind($fh);
fwrite($fh, $new_content);
} finally {
flock($fh, LOCK_UN);
fclose($fh);
}
return true;
}
// 使用示例
read_write_atomic('/tmp/data.json', function($old) {
$data = json_decode($old, true) ?? [];
$data['last_update'] = time();
return json_encode($data, JSON_UNESCAPED_UNICODE);
});
「加鎖 → 讀取 → 截斷 → 重置指針 → 寫入 → 解鎖」是文件級原子讀寫的標準範式。掌握細節、封裝復用,能有效避免併發數據錯亂、文件損壞等疑難雜癥。