首页 > 编程笔记 > Go语言笔记 阅读:5

Go语言flock文件锁的用法(附带实例)

当多个进程同时操作同一文件时,很容易导致文件中的数据混乱。这时要采用一些技术平衡这些冲突,文件锁(flock)就是这些技术之一。

flock 属于建议性锁,不具备强制性。一个进程使用 flock 将文件锁住,另一个进程可以直接操作已锁住的文件,修改文件中的数据。这是因为 flock 只是检测文件是否被加锁,即使一个文件已经加锁,但另一个进程仍要写入数据,内核也不会阻止这个进程的写入操作。这就是建议性锁的内核处理策略。

flock 主要有 3 种操作类型:
对于 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)
}
通过控制台打印的异常信息,即可得到同一文件在指定周期内只允许一个进程访问的结论。

相关文章