Go语言flock文件锁的用法(附带实例)
当多个进程同时操作同一文件时,很容易导致文件中的数据混乱。这时要采用一些技术平衡这些冲突,文件锁(flock)就是这些技术之一。
flock 属于建议性锁,不具备强制性。一个进程使用 flock 将文件锁住,另一个进程可以直接操作已锁住的文件,修改文件中的数据。这是因为 flock 只是检测文件是否被加锁,即使一个文件已经加锁,但另一个进程仍要写入数据,内核也不会阻止这个进程的写入操作。这就是建议性锁的内核处理策略。
flock 主要有 3 种操作类型:
对于 flock,最常见的例子就是使用 Nginx。进程运行后把当前 PID 写入文件,如果文件已经存在,即前一个进程还没有退出,那么 Nginx 就不会重新启动,所以 flock 还可以检测进程是否存在。
因为 Windows 系统不支持 pid 锁,所以要在 Linux 或 Mac 系统下演示。
【实例】演示同时启动 10 个 goroutinue,但在程序运行过程中,只有一个 goroutine 能获得 flock,其他的 goroutinue 在获取不到 flock 后,抛出异常信息。
flock 属于建议性锁,不具备强制性。一个进程使用 flock 将文件锁住,另一个进程可以直接操作已锁住的文件,修改文件中的数据。这是因为 flock 只是检测文件是否被加锁,即使一个文件已经加锁,但另一个进程仍要写入数据,内核也不会阻止这个进程的写入操作。这就是建议性锁的内核处理策略。
flock 主要有 3 种操作类型:
- LOCK_SH:共享锁,多个进程可以使用同一把锁,常被用作读共享锁;
- LOCK_EX:排他锁,同时只允许一个进程使用,常被用作写锁;
- LOCK_UN:释放锁。
对于 flock,最常见的例子就是使用 Nginx。进程运行后把当前 PID 写入文件,如果文件已经存在,即前一个进程还没有退出,那么 Nginx 就不会重新启动,所以 flock 还可以检测进程是否存在。
因为 Windows 系统不支持 pid 锁,所以要在 Linux 或 Mac 系统下演示。
【实例】演示同时启动 10 个 goroutinue,但在程序运行过程中,只有一个 goroutine 能获得 flock,其他的 goroutinue 在获取不到 flock 后,抛出异常信息。
package main import ( "fmt" "os" "sync" "syscall" "time" ) // 文件锁 type FileLock struct { dir string f *os.File } func New(dir string) *FileLock { return &FileLock{ dir: dir, } } // 加锁 func (l *FileLock) 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) if err != nil { return fmt.Errorf("cannot lock directory %s - %s", l.dir, err) } return nil } // 释放锁 func (l *FileLock) Unlock() error { defer l.f.Close() return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN) } func main() { test_file_path, _ := os.Getwd() locked_file := test_file_path wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) go func(num int) { lock := New(locked_file) err := lock.Lock() if err != nil { wg.Done() fmt.Println(err.Error()) return } fmt.Printf("output : %d\n", num) wg.Done() }(i) } wg.Wait() time.Sleep(2 * time.Second) }通过控制台打印的异常信息,即可得到同一文件在指定周期内只允许一个进程访问的结论。