linux flock与windows filestream.lock均为advisory锁,但api行为差异大:linux重复flock不报错,windows重复lock抛ioexception;跨平台应统一用filestream+fileshare.none+异常捕获模拟advisory锁。

Linux 上 flock 和 Windows 上 FileStream.Lock 行为不一致
Linux 的 flock 是 advisory(顾问锁),依赖所有参与者主动检查;Windows 的 FileStream.Lock 默认是 mandatory(强制锁),但仅对同一句柄有效,跨进程时实际也退化为 advisory。两者底层语义接近,但 API 设计和错误表现差异大——比如 Linux 下重复 flock 不报错,Windows 下对已锁定文件再次 Lock 会直接抛 IOException。
实操建议:
- 别混用
flock(通过 P/Invoke)和FileStream.Lock:它们锁的不是同一个内核对象,完全不互斥 - 统一用
FileStream+FileShare.None+ 异常捕获,这是跨平台最稳的 advisory 锁模拟方式 - Linux 下若用
flock,必须确保所有进程都调用fcntl(F_SETLK),且不能依赖close()自动释放(因为 .NET 的FileStream可能延迟 dispose)
FileStream 构造时 FileShare 参数决定锁粒度
很多人以为加了 Lock() 就锁住了,其实真正起作用的是打开文件时的 FileShare。Windows 和 Linux 上,new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None) 才能阻止其他进程以读/写方式打开该文件;而 FileShare.Read 或 FileShare.Write 会让锁形同虚设。
实操建议:
- 锁文件必须用
FileShare.None,哪怕只是想防止写入——因为 Linux 的flock和 Windows 的句柄锁都基于“打开权限”协商 - 不要在
using块里只做Lock()而不保持流打开:一旦FileStream被 dispose,锁立即释放 - 若需长时间持有锁但又不想阻塞 I/O,可用
FileStream打开后立刻Lock(0, long.MaxValue),然后把流存在静态字典里(注意线程安全和泄漏)
跨平台锁失败时的典型错误信息和应对
Linux 下常见 IOException: The process cannot access the file because it is being used by another process(这是 .NET 在 Linux 上对 EBUSY 的翻译,实际和 Windows 错误码一样);Windows 下还可能遇到 Access to the path is denied(权限不足或防病毒软件拦截)。
实操建议:
- 捕获
IOException,检查ex.HResult:Windows 上-2147024864(0x80070020)表示被占用,Linux 上HResult通常为 0,得靠ex.Message.Contains("used by another process") - 避免轮询重试时用固定 sleep:改用指数退避(如 10ms → 30ms → 100ms),否则在容器或高并发场景下容易打满 CPU
- 测试时务必在 WSL、Docker(alpine/debian)、Windows Server 上分别验证——尤其是 Docker 默认以 root 运行,而生产环境常以非 root 用户跑,权限差异会导致锁行为突变
为什么不用 MemoryMappedFile 或临时文件做协调
MemoryMappedFile 在 Linux 上需要 /dev/shm 支持,且跨进程命名规则与 Windows 不兼容;临时文件(如 file.lock)看似简单,但存在竞态:两个进程同时检查文件不存在 → 同时创建 → 都认为自己拿到锁。
实操建议:
- 真要临时文件方案,必须用原子操作:
File.Create("file.lock", 0, FileOptions.DeleteOnClose | FileOptions.Asynchronous),并捕获IOException判断是否被抢占(Linux/macOS 下成功则拿到锁,Windows 下需额外判断Win32Exception.NativeErrorCode == 5) - 如果业务允许,优先用外部协调服务(Redis 的
SET key val NX PX 30000)代替文件锁——文件锁本质是妥协方案,只适合无外部依赖的嵌入式或单机工具 - 所有锁逻辑必须带超时:无论是
FileStream的打开超时,还是业务层的租约续期,否则一个崩溃进程可能永久霸占锁
真正麻烦的不是怎么加锁,而是怎么让所有调用方都遵守同一套打开+检查+释放流程。漏掉任意一环,顾问锁就变成幻觉。










