You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP如何实现文件锁定后读取内容再覆盖(截断)写入?

问题根因

w+模式的固有特性导致了这个问题:调用fopen($file, "w+")时,PHP会在打开文件的瞬间直接清空文件全部内容,这个操作发生在你调用flock加锁之前,因此你拿到独占锁后读取到的内容必然是空的。更严重的是多进程场景下,即使进程没有抢到锁,只要执行了fopen("w+")也会直接清空文件,完全不符合你先读取再修改的需求。

你现有代码还存在两处明显错误:

  • explode的第三个参数错误传入了锁常量LOCK_EX,这会导致分割最多只返回2个元素,不符合队列分割的预期
  • 没有处理文件指针位置和旧内容残留的问题,直接写入会导致新内容比旧内容短时,文件尾部残留旧数据

修正方案

改用r+模式打开文件,该模式支持读写且不会在打开时清空文件,流程调整为:打开文件→加独占锁→读取内容→处理逻辑→将文件指针移到开头→清空文件内容→写入新内容→解锁→关闭文件。

修正后的代码如下:

$fp = fopen($file, "r+");
if(flock($fp, LOCK_EX)) {
    // 用fstat获取文件真实大小,避免filesize缓存问题
    $fstat = fstat($fp);
    $filesize = $fstat['size'];
    $content = fread($fp, $filesize);
    echo $content;

    // 修正explode参数,按换行分割队列
    $job_queue = explode("\n", $content);
    $next_job = array_shift($job_queue);

    // 指针移到文件开头
    rewind($fp);
    // 清空文件原有内容
    ftruncate($fp, 0);
    // 写入新的队列内容
    fwrite($fp, implode("\n", $job_queue));
    
    flock($fp, LOCK_UN);
} else {
    echo '<br>Error: cannot lock job queue file';
}
fclose($fp);

补充注意事项

  • 不要在加锁前读取文件内容,否则读取到的数据可能已经被其他进程修改,出现竞态问题
  • 如果队列内容可能包含空行,建议使用更可靠的序列化方式存储队列数据,避免换行分割出现异常
  • 高并发场景下可以给flock加LOCK_NB参数实现非阻塞锁,避免进程长时间挂起等待

内容的提问来源于stack exchange,提问作者jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 19:15:01