在php开发过程中,当多个请求同时需要对同一个文件进行写入操作时,如果不做任何处理,后写入的内容会直接覆盖先写入的内容,导致数据丢失或者内容错乱。这时候就需要用到php的文件锁机制,通过flock函数来控制文件的访问权限,避免并发写入冲突。

flock函数基本说明
php的flock()函数用于对已经打开的文件指针加锁或者解锁,函数的基本语法如下:
<?php flock(resource $stream, int $operation, int &$wouldblock = null): bool ?>
参数说明:
$stream:已经打开的文件资源句柄$operation:锁的类型,可选值如下$wouldblock:可选参数,如果加锁会阻塞并且锁类型是LOCK_NB,该参数会被设置为1
锁类型的具体可选值:
| 锁类型常量 | 说明 |
|---|---|
| LOCK_SH | 共享锁,多个进程可以同时持有,用于读取文件场景 |
| LOCK_EX | 排他锁,同一时间只能有一个进程持有,用于写入文件场景 |
| LOCK_UN | 释放锁 |
| LOCK_NB | 非阻塞模式,加锁失败时不等待直接返回,需要和LOCK_SH或LOCK_EX配合使用 |
防止并发写入的实现示例
下面是一个简单的文件写入场景,使用排他锁保证同一时间只有一个进程可以写入文件:
<?php
// 要写入的文件路径
$filePath = 'test.txt';
// 写入的内容
$content = "当前时间:" . date('Y-m-d H:i:s') . PHP_EOL;
// 以追加写入模式打开文件,如果文件不存在则创建
$fp = fopen($filePath, 'a');
if (!$fp) {
die('打开文件失败');
}
// 尝试获取排他锁,阻塞等待直到获取锁
if (flock($fp, LOCK_EX)) {
// 获取锁成功,执行写入操作
fwrite($fp, $content);
// 写入完成后释放锁
flock($fp, LOCK_UN);
} else {
echo '获取文件锁失败';
}
// 关闭文件句柄
fclose($fp);
?>
如果需要非阻塞模式,避免进程长时间等待锁,可以修改加锁代码:
<?php
$filePath = 'test.txt';
$content = "当前时间:" . date('Y-m-d H:i:s') . PHP_EOL;
$fp = fopen($filePath, 'a');
if (!$fp) {
die('打开文件失败');
}
// 使用非阻塞排他锁,加锁失败直接返回
if (flock($fp, LOCK_EX | LOCK_NB)) {
fwrite($fp, $content);
flock($fp, LOCK_UN);
} else {
echo '当前文件被其他进程占用,稍后重试';
}
fclose($fp);
?>
读取场景的共享锁使用
如果是读取文件的场景,可以使用共享锁,多个进程可以同时读取,不会互相阻塞:
<?php
$filePath = 'test.txt';
$fp = fopen($filePath, 'r');
if (!$fp) {
die('打开文件失败');
}
// 获取共享锁,多个进程可以同时持有
if (flock($fp, LOCK_SH)) {
$content = fread($fp, filesize($filePath));
echo "文件内容:" . $content;
flock($fp, LOCK_UN);
} else {
echo '获取共享锁失败';
}
fclose($fp);
?>
使用注意事项
- flock函数依赖文件句柄,必须先通过
fopen()打开文件再调用加锁函数,锁会在文件句柄关闭时自动释放 - flock是建议性锁,不是强制性锁,也就是说如果其他进程不调用flock直接操作文件,还是会出现冲突,需要所有操作文件的进程都遵循加锁规则
- 在Windows系统下,LOCK_NB参数可能不生效,加锁操作会默认阻塞
- 不要对使用
file_get_contents()这类函数直接读取的文件加锁,因为这些函数内部没有暴露文件句柄,无法使用flock控制 - 如果是高并发场景需要频繁操作文件,建议优先考虑使用数据库或者专门的缓存组件来存储数据,文件锁的性能不如这些方案
常见问题说明
很多开发者会遇到加锁后还是出现冲突的情况,通常是因为以下原因:
- 部分写入操作没有加锁,只有部分进程遵循了加锁规则
- 文件打开模式不对,比如以写入模式打开文件时,有些系统会直接清空文件内容,导致之前的数据丢失
- 锁的作用范围不对,flock锁是基于文件句柄的,不同的文件句柄对应同一个文件时,锁的生效规则需要额外注意
只要遵循正确的加锁流程,所有操作同一文件的进程都使用flock控制访问,就可以有效避免并发写入冲突的问题。