windows 不支持 syscall.flock,因其缺失 posix flock(2) 系统调用,调用直接返回 enosys;go 1.19+ 的 os.file.lock 底层用 lockfileex 实现强制独占锁,但仅限 windows,跨平台不可用;gofrs/flock 默认不安全,需显式启用 o_excl 和 withexclusivecreate();lockfileex 要求 createfile 的 dwsharemode 必须为 0,否则报 access is denied;锁文件需写入 pid 并检查进程存活,防止僵尸锁。

syscall.Flock 在 Windows 上根本不可用,调用会直接返回 ENOSYS 或 panic,这不是代码写错了,而是系统级缺失。Windows 没有 POSIX 的 flock(2) 系统调用,必须换方案。
为什么 os.File.Lock 能用但又不能全信
从 Go 1.19 开始,os.File.Lock 和 os.File.Unlock 在 Windows 上已稳定支持,底层调用的就是 LockFileEx,能实现真正的强制独占锁;但在 Linux/macOS 上,它目前仍会 panic —— 文档没说清楚,实际是“Windows 专用”。
你如果只跑 Windows,可以用它;但只要有一点跨平台需求(比如本地开发用 macOS、部署到 Windows),就别碰这个 API,否则 CI 直接炸。
别用 github.com/gofrs/flock 默认行为
这个库确实封装了 Windows fallback,但它默认创建锁文件时用的是 os.O_CREATE | os.O_RDWR,没加 O_EXCL。这意味着:两个进程同时调 TryLock,可能都成功打开同一个锁文件,导致“假锁成功”。
必须手动确保锁文件创建带 O_EXCL:
- 自己调
os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600),靠原子性判断是否抢到锁 - 或确认你用的
flock版本 ≥ 0.8.1,并显式传flock.WithExclusiveCreate() - 锁文件路径不能和业务文件相同(比如不要锁
config.json自身),必须另建config.json.lock
LockFileEx 调用时 HANDLE 权限必须为 0
这是 Windows 锁定机制最常踩的坑:LockFileEx 要求文件句柄是以 无共享模式 打开的,即 CreateFile 的 dwShareMode 参数必须为 0(不是 FILE_SHARE_READ 或其它)。
Go 标准库的 os.OpenFile 默认会设 FILE_SHARE_READ | FILE_SHARE_WRITE,所以不能直接传 *os.File 给 LockFileEx —— 必须用 golang.org/x/sys/windows.CreateFile 重开句柄:
通过 SSH 隧道从 Android 设备在互联网上对 Windows PC 实现完整远程控制的设置与使用指南,适用于远程桌面访问、安全 RDP 等。
h, err := windows.CreateFile(
windows.StringToUTF16Ptr(path),
windows.GENERIC_READ|windows.GENERIC_WRITE,
0, // ← 这里必须是 0,否则 LockFileEx 返回 ERROR_ACCESS_DENIED
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0,
)
漏掉这一步,错误信息是 Access is denied,而不是锁被占用,容易误判。
锁文件残留比锁失败更危险
用 O_EXCL 创建锁文件后,进程崩溃或被 kill -9,锁文件不会自动消失。下次启动时会误判“锁被占用”,其实只是个僵尸文件。
必须做两件事:
- 锁文件内容写入
os.Getpid()和时间戳,方便诊断 - 每次尝试获取锁前,先
os.Stat锁文件,读取 PID 并用kill -0(Unix)或OpenProcess(Windows)检查该进程是否还活着
这个检测逻辑没法省——跨进程锁的本质就是状态协调,不是靠一次系统调用就万事大吉。










