flock($fp, LOCK_EX)返回true却未锁住,根本原因是其为建议性锁,仅当所有进程均主动调用flock才生效;若漏调、绕过句柄(如file_put_contents)、或跨NFS/容器时fd不共享,锁即失效。

为什么flock($fp, LOCK_EX)有时返回true却没锁住
根本原因是 flock 是建议性锁(advisory lock),它不强制拦截其他进程,只依赖所有参与者主动调用 flock。只要有一个脚本漏掉加锁步骤,或用 file_put_contents 直接写文件(绕过句柄),锁就完全失效。
常见错误场景包括:
- ThinkPHP 的
Cache::set()或FileCache驱动默认不调用flock,必须手动包装 - 日志写入用了
error_log()或file_put_contents($file, $data, FILE_APPEND),这些函数不检查锁 - 多个 PHP 进程打开的是同一路径但不同
$fp句柄,而flock锁定的是句柄,不是路径
验证是否真生效:开两个终端,同时运行带 flock 的脚本,观察第二个是否阻塞或报错;再用 echo "test" >> lock.txt 从 shell 直接追加——如果成功,说明锁未覆盖全部访问点。
LOCK_EX + LOCK_NB 怎么模拟超时并避免死等
flock 默认阻塞,没有内置超时。硬等可能卡住整个请求,尤其在 Swoole 常驻进程中会拖垮 Worker。必须自己实现非阻塞+轮询+超时控制。
立即学习“PHP免费学习笔记(深入)”;
正确做法是:
- 用
fopen打开文件后,立刻调用flock($fp, LOCK_EX | LOCK_NB) - 失败时用
usleep(10000)(10ms)短暂停顿,再重试,累计不超过预设阈值(如 2 秒) - 不要在
flock区间内做远程请求、大文件读写、复杂计算等耗时操作 - 务必用
try...finally保证flock($fp, LOCK_UN)和fclose($fp)一定执行
示例关键逻辑:
$fp = fopen('/tmp/counter.lock', 'c');
$attempts = 0;
while (!flock($fp, LOCK_EX | LOCK_NB) && $attempts < 200) {
usleep(10000);
$attempts++;
}
if ($attempts >= 200) {
throw new RuntimeException('Acquire lock timeout');
}
// ... 临界区操作
flock($fp, LOCK_UN);
fclose($fp);
在 Swoole 或 Docker 环境下 flocK 为什么经常失效
失效不是 bug,是设计使然:flock 依赖内核对「文件描述符」的管理,而 Swoole Worker 复用进程、Docker 容器挂载卷、NFS 共享路径都会破坏这一前提。
典型表现:
- Swoole 中
flock始终返回false,或LOCK_UN释放了别人的锁 - Docker 挂载宿主机目录时,
flock在容器内无效(尤其 Windows/macOS 主机 + Linux 容器) - NFS 上多个节点调用
flock,锁行为不可靠,甚至被忽略
根本规避方式:
- 绝对不在
onWorkerStart提前fopen并长期持锁 - 每个请求生命周期内独立完成
fopen → flock → 操作 → flock(LOCK_UN) → fclose - 跨机器或容器部署,直接放弃
flock,改用 RedisSET key value NX EX 10+ Lua 脚本保证原子性
该用 flock 还是 ThinkPHP 的 Cache::lock
不是二选一,而是用途完全不同。
flock 保护的是「一个具体文件的读写完整性」,比如日志追加、JSON 配置更新、计数器文件写入;而 Cache::lock('order_123') 是业务级抽象锁,保护的是「一段逻辑的执行互斥」,比如扣库存、发通知。
关键差异:
-
flock是系统调用,轻量但脆弱;Cache::lock底层用临时文件 +rename原子性模拟,更健壮但有 stat 轮询开销 -
flock不支持超时等待,Cache::lock()->wait(5)->block(10)可控性强 - 若用
Cache::driver('file'),它仍生成锁文件,只是封装了创建、检测、清理逻辑,不是绕过文件系统
一句话判断:你要锁的是“数据落盘动作”,用 flock;你要锁的是“不让两个请求同时处理订单 123”,用 Cache::lock。混用或误判,是并发问题反复出现的根源。



















