
本文介绍在 Windows 平台下使用 Go 语言实现真正独占文件锁的可靠方法,解决 syscall.Flock 不可用问题,通过调用 Windows 原生 API(LockFileEx)实现跨进程、阻塞/非阻塞式字节范围锁,并提供可直接运行的封装代码与关键注意事项。
本文介绍在 windows 平台下使用 go 语言实现真正独占文件锁的可靠方法,解决 `syscall.flock` 不可用问题,通过调用 windows 原生 api(`lockfileex`)实现跨进程、阻塞/非阻塞式字节范围锁,并提供可直接运行的封装代码与关键注意事项。
在 Windows 系统中,POSIX 风格的 flock() 或 Go 标准库中的 syscall.Flock 均不可用——它仅在类 Unix 系统上有效,Windows 使用完全不同的文件锁定机制。若需实现真正的独占访问控制(即其他进程无法以写方式打开或修改该文件),必须调用 Windows 原生 API:LockFileEx(推荐)或 LockFile。二者均作用于打开的文件句柄(HANDLE),支持对文件任意字节范围加锁,并可指定是否阻塞、是否为共享锁或独占锁。
LockFileEx 是更现代、功能更完整的接口,支持重叠 I/O 和超时控制,且能返回详细的错误信息(如 ERROR_IO_PENDING),是生产环境首选。其核心参数包括:
- hFile: 已通过 CreateFile 打开、具备 FILE_SHARE_READ | FILE_SHARE_WRITE 之外权限的句柄(通常需 GENERIC_READ | GENERIC_WRITE);
- dwFlags: 组合标志,如 LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY(立即失败而非阻塞);
- nNumberOfBytesToLockLow/High: 锁定字节长度(0xFFFFFFFF, 0xFFFFFFFF 表示锁定整个文件);
- lpOverlapped: 指向 OVERLAPPED 结构体的指针(含偏移量,通常设为 0,0 表示从文件开头锁定)。
以下是一个完整、线程安全、可复用的 Go 封装示例(需 golang.org/x/sys/windows):
package main
import (
"errors"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type FileLock struct {
handle windows.Handle
}
// NewFileLock 创建一个基于 Windows LockFileEx 的独占锁
func NewFileLock(path string) (*FileLock, error) {
// 注意:必须禁用共享模式,否则 LockFileEx 将失败(Access is denied)
h, err := windows.CreateFile(
windows.StringToUTF16Ptr(path),
windows.GENERIC_READ|windows.GENERIC_WRITE,
0, // ← 关键:不设置任何 SHARE_* 标志!
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0,
)
if err != nil {
return nil, err
}
return &FileLock{handle: h}, nil
}
// Lock 获取独占锁(阻塞直到成功,除非系统中断)
func (fl *FileLock) Lock() error {
var ol windows.Overlapped
// 锁定整个文件(0 到 2^64-1)
err := windows.LockFileEx(fl.handle,
windows.LOCKFILE_EXCLUSIVE_LOCK,
0,
0xFFFFFFFF, 0xFFFFFFFF,
&ol)
if err != nil {
return err
}
return nil
}
// Unlock 释放锁(注意:CloseHandle 即隐式解锁)
func (fl *FileLock) Unlock() error {
return windows.CloseHandle(fl.handle)
}
// 示例用法
func main() {
lock, err := NewFileLock(`C:\temp\shared.dat`)
if err != nil {
panic(err)
}
defer lock.Unlock()
if err = lock.Lock(); err != nil {
panic("无法获取独占锁: " + err.Error())
}
// ✅ 此时其他进程调用 CreateFile(..., 0, ...) 将失败(ERROR_SHARING_VIOLATION)
// 可安全执行读写操作...
}⚠️ 关键注意事项:
- 共享标志必须为 0:CreateFile 的 dwShareMode 参数严禁传入 FILE_SHARE_READ 或 FILE_SHARE_WRITE,否则 LockFileEx 会返回 ERROR_ACCESS_DENIED(即提问中遇到的 “Access is denied” 错误);
- 锁与句柄绑定:锁生命周期与文件句柄强绑定;关闭句柄(CloseHandle)即自动释放锁;
- 不支持 fork 场景:Windows 无 fork,无需考虑锁继承问题;
- 避免死锁:若需非阻塞尝试,请使用 LOCKFILE_FAIL_IMMEDIATELY 并检查 ERROR_IO_PENDING 以外的错误;
- 权限要求:调用进程需对目标文件具有读写访问权限(ACL 控制)。
综上,绕过 syscall.Flock 的限制,在 Windows 上实现健壮文件独占锁的唯一可靠路径是直接调用 LockFileEx。本方案已在实际日志聚合、配置热更新等多进程协同场景中验证稳定,建议封装为独立模块并在 init() 中校验 Windows 平台兼容性。










