首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 讀取文件後安全清空:文件鎖 + 指針控制完整示例

     編輯:PHP 相關知識

在高併發場景下,讀取文件內容並清空(如日誌輪轉、臨時數據交換)需注意原子性與數據一致性。本文提供一個使用 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 循環)或臨時文件交換。
  • NFS/網絡文件系統: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);
});

總結

「加鎖 → 讀取 → 截斷 → 重置指針 → 寫入 → 解鎖」是文件級原子讀寫的標準範式。掌握細節、封裝復用,能有效避免併發數據錯亂、文件損壞等疑難雜癥。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved