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 夜读