共享锁允许多个事务并发读,但禁止修改;排他锁独占数据,禁止其他事务读或写。二者互斥,构成读写隔离基础,且均属咨询锁,依赖所有进程主动遵守规则。

共享锁与排他锁的本质区别
共享锁(LOCK_SH)允许多个进程同时持有,只用于读操作;只要没有排他锁存在,多个进程可以并行读取同一文件。排他锁(LOCK_EX)是互斥的,任一时刻最多一个进程能获得,且它会阻塞所有其他共享锁和排他锁的获取请求——这是读写隔离的基础。
flock 的咨询锁特性决定行为边界
flock 实现的是咨询锁(advisory lock),不是内核强制锁。它的生效前提是:所有访问该文件的进程都主动调用 flock 并遵守规则。如果某个进程绕过 flock 直接读写,锁就形同虚设。这一点在日志写入、计数器更新等场景中尤为关键——漏掉一次加锁,整个并发控制就可能失效。
多进程争抢锁的典型死锁场景
真实死锁在纯 flock 场景中极少发生,因为 flock 本身不支持“锁升级”(比如先持共享锁再转为排他锁),也不允许跨文件锁等待链。但以下两类情况容易被误认为死锁,实则是阻塞或逻辑陷阱:
- 单文件循环等待:进程 A 持有文件 X 的 LOCK_EX,进程 B 尝试对 X 加 LOCK_EX,B 会一直阻塞,直到 A 释放锁。这不是死锁,只是正常排队;若 A 崩溃未释放,锁会在进程退出时由内核自动清理。
- 多文件加锁顺序不一致:进程 A 先锁 file1 再锁 file2,进程 B 反过来先锁 file2 再锁 file1。虽然 flock 本身不跨文件维持锁状态,但如果上层业务逻辑用多个文件模拟资源池(如临时锁文件 + 数据文件),且加锁顺序混乱,就可能造成互相等待——这属于应用层设计问题,而非 flock 机制缺陷。
- 非阻塞使用不当导致忙等+资源耗尽:用 LOCK_NB 循环尝试加锁但未设休眠或超时,CPU 占用飙升,看起来像“卡死”,实际是活锁倾向。
安全使用 flock 的关键实践
避免争抢失控和隐性阻塞,重点不在“防死锁”,而在“控行为”:
- 写操作一律用 LOCK_EX | LOCK_NB 配合超时重试,不要依赖默认阻塞
- 读操作若需强一致性(比如读后再写),不要只加 LOCK_SH,应直接升级为 LOCK_EX,避免中间被其他写进程插入
- 锁必须在 同一文件句柄生命周期内获取与释放,不要在 fclose 后还试图解锁
- 始终显式调用 flock($fp, LOCK_UN),不要依赖 fclose 自动释放——尤其在异常分支中容易遗漏
- 锁文件建议单独创建(如
counter.lock),不与数据文件混用,降低语义耦合
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











