go中flock仅适用于linux/macos,windows需用lockfileex;flock是建议性锁,绑定fd,进程退出自动释放,写操作必须用lock_ex,阻塞需加lock_nb避免卡死。

Go 中用 flock 实现文件级互斥写入
Go 标准库不提供跨进程的文件锁,flock 是最直接、最轻量的方案,适用于 Linux/macOS。Windows 需换用 syscall.LockFileEx(下文另说)。flock 锁是 advisory lock(建议性锁),依赖所有参与者主动调用,但只要统一用它,就能可靠防止并发写冲突。
常见错误:直接用 os.OpenFile(..., os.O_CREATE|os.O_WRONLY) 反复打开写入,没加锁——多个进程同时写同一文件,必然内容错乱或覆盖。
-
flock锁绑定在文件描述符上,进程退出或 fd 关闭时自动释放,不用手动 unlock(除非显式调用) - 锁类型分
LOCK_EX(排他锁)和LOCK_SH(共享锁),写操作必须用LOCK_EX - 阻塞 vs 非阻塞:用
LOCK_EX会阻塞,加LOCK_NB可立即返回错误(如resource temporarily unavailable) - 示例片段:
fd, _ := os.OpenFile("data.txt", os.O_RDWR|os.O_CREATE, 0644) defer fd.Close() syscall.Flock(int(fd.Fd()), syscall.LOCK_EX) // 此处安全写入 fd.Write([]byte("new content"))
Windows 下如何替代 flock
Windows 没有 flock 系统调用,得用 syscall.LockFileEx,且需注意句柄权限和锁范围。标准库 golang.org/x/sys/windows 提供了封装,但底层仍需手动管理。
容易踩的坑:LockFileEx 要求文件句柄以 GENERIC_READ | GENERIC_WRITE 打开,并且锁区域不能为 0;若只传 0 作偏移/长度,会失败并报 ERROR_INVALID_PARAMETER。
- 必须用
windows.CreateFile打开文件(不能用os.OpenFile),否则句柄无足够权限 - 锁范围推荐设为整个文件:
numberOfBytesToLockLow = 1,numberOfBytesToLockHigh = 0(即锁 1 字节,但配合LOCKFILE_EXCLUSIVE_LOCK实际效果等价于全文件排他) - 释放锁需调用
windows.UnlockFileEx,不能依赖句柄关闭自动释放 - 跨平台代码建议封装一个
FileLock接口,Linux 用syscall.Flock,Windows 用windows.LockFileEx
os.Chmod 和 os.Chown 会影响 flock 吗?
不影响。flock 锁与文件元数据无关,只关联打开的文件描述符。改权限、属主、甚至重命名文件,只要 fd 未关闭,锁依然有效。但要注意:如果进程先 flock,再 os.Rename 原文件,其他进程按原路径 open 得到的是新文件(无锁),旧 fd 还锁着已重命名的文件——这会造成逻辑错位。
- 重命名前应先释放锁,再 rename,再重新 open + flock(如需继续操作)
- 不要依赖文件路径做锁标识,锁的实际载体是 inode + fd,路径只是入口
-
os.Truncate不影响锁,os.Remove也不立即释放锁(fd 仍有效,直到 close)
为什么不用 sync.Mutex?
sync.Mutex 只作用于当前进程内 goroutine,对多进程场景完全无效。如果你启动了两个独立的 Go 程序,都往同一个文件写,sync.Mutex 彼此毫无感知,照样冲突。
典型误用场景:把 sync.Mutex 包在全局变量里,以为“加了锁就安全”,结果部署时启了多个实例(比如 systemd 多 service、K8s 多 pod、或简单地后台跑两个 ./app),锁完全失效。
- 进程间同步必须靠 OS 级原语:flock / LockFileEx / POSIX semaphores / Unix domain socket 等
- 如果业务允许,更稳妥的做法是引入外部协调服务(如 etcd 分布式锁),但对单机多进程,
flock仍是首选 - 注意:容器环境里,多个容器默认不共享文件系统,
flock仅在挂载同一 volume 且使用相同路径时才生效
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











