golang 的文件锁操作
这篇文章给大家介绍一下 golang 的文件锁。我们在使用 golang 开发程序的时候,经常会出现多个 goroutine 操作同一个文件(或目录)的时候,如果不加锁,很容易导致文件中的数据混乱,于是,Flock 应运而生。
Flock 是对于整个文件的建议性锁(不强求 goroutine 遵守),如果一个 goroutine 在文件上获取了锁,那么其他 goroutine 是可以知道的。默认情况下,当一个 goroutine 将文件锁住,另外一个 goroutine 可以直接操作被锁住的文件,原因在于 Flock 只是用于检测文件是否被加锁,针对文件已经被加锁,另一个 goroutine 写入数据的情况,内核不会阻止这个 goroutine 的写入操作,也就是建议性锁的内核处理策略。
#函数
1 2 3 |
import "syscall" func Flock(fd int, how int) (err error) |
Flock 位于 syscall 包中,fd 参数指代文件描述符,how 参数指代锁的操作类型。
how 主要的参数类型:
LOCK_SH,共享锁,多个进程可以使用同一把锁,常被用作读共享锁; LOCK_EX,排他锁,同时只允许一个进程使用,常被用作写锁; LOCK_NB,遇到锁的表现,当采用排他锁的时候,默认 goroutine 会被阻塞等待锁被释放,采用 LOCK_NB 参数,可以让 goroutine 返回 Error; LOCK_UN,释放锁; 示例 下面的例子来自于 NSQ,位于 nsq/internal/dirlock,用于实现对目录的加锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// +build !windows package dirlock import ( "fmt" "os" "syscall" ) // 定义一个 DirLock 的struct type DirLock struct { dir string // 目录路径,例如 /home/XXX/go/src f *os.File // 文件描述符 } // 新建一个 DirLock func New(dir string) *DirLock { return &DirLock{ dir: dir, } } // 加锁操作 func (l *DirLock) Lock() error { f, err := os.Open(l.dir) // 获取文件描述符 if err != nil { return err } l.f = f err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) // 加上排他锁,当遇到文件加锁的情况直接返回 Error if err != nil { return fmt.Errorf("cannot flock directory %s - %s", l.dir, err) } return nil } // 解锁操作 func (l *DirLock) Unlock() error { defer l.f.Close() // close 掉文件描述符 return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN) // 释放 Flock 文件锁 } |
#总结 Flock 是建议性的锁,使用的时候需要指定 how 参数,否则容易出现多个 goroutine 共用文件的问题 how 参数指定 LOCK_NB 之后,goroutine 遇到已加锁的 Flock,不会阻塞,而是直接返回错误
via Go 夜读