跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何在 Windows 上实现文件独占锁(Go 语言兼容方案)

本文介绍在 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 平台兼容性。

相关文章