golang文件锁,目录锁,syscall包的使用

先说结论

1. golang提供了syscall包来实现文件/目录的加锁,解锁

2. syscall包属于文件锁,是比较底层的技术,并不能在所有操作系统上完全实现,linux上实现了,windows下面就没有

3. 加锁时调用syscall.Flock(fd,syscall.LOCK_EX),解锁时调用syscall.Flock(fd, syscall.LOCK_UN)

4. 加锁成功后,对加锁的文件fd进行Close()操作同样会释放锁,切记

代码实现

锁的定义如下内部两个变量:文件/目录的全路径名,文件对象

// 文件锁/目录锁
type DirLock struct {
    dir     string        // 文件/目录的全路径名
    f       *os.File    // 文件对象
}

加锁的实现

核心代码是 syscall.Flock(int(f.Fd()), LOCK_EX|syscall.LOCK_NB),注意其中的标记

LOCK_EX :加锁标记。只有一个进程能加锁成功,其他进程再尝试加锁时会阻塞,等同于我们常用的写锁

LOCK_NB :不阻塞标记。如果其他进程已加锁成功,自己去尝试加锁时就不再阻塞,而是直接返回错误

// 加锁
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)
    if err != nil {
        return err
    }
    return nil
}

解锁的实现

核心代码是 syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN)

LOCK_UN :解锁标记。如果自己已经加锁成功,可以用此标记去解锁

// 解锁
func (l *DirLock) Unlock() error {
    defer l.f.Close() // 关闭文件

    return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN) // LOCK_UN表解锁
}

实验

我们建立5个协程,每秒去尝试加锁一次,失败则1秒后重试,成功则持续2秒后解锁

核心代码如下

    // 5个协程,都尝试对目录加锁,加锁失败的就重试,加锁成功的2秒后释放
    for i := 0; i < 5; i++ {
        wg.Add(1)

        go func(num int) {
            dirLock := New(dir)
            ticker := time.NewTicker(time.Second) // 定时器每秒尝试1次
            for {
                select {
                case <-ticker.C:
                    {
                        err := dirLock.Lock() // 加锁尝试
                        if err != nil {
                            fmt.Printf("lock dir failed, goroutine num=%d, err=%s \n", num, err.Error())
                            continue
                        }
                        fmt.Println("lock dir succeed, goroutine num=", num)
                        goto end
                    }
                }
            }

            end:
            time.Sleep(time.Second*2)
            dirLock.Unlock() // 解锁
            wg.Done()
        }(i)
    }
    wg.Wait()

实验结果如下图

完整代码

package main
import (
	"fmt"
	"os"
	"sync"
	"syscall"
	"time"
)

// 目录锁
type DirLock struct {
	dir 	string		// 目录的全路径名
	f   	*os.File	// 文件对象
}

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)
	if err != nil {
		return err
	}
	return nil
}

// 释放锁
func (l *DirLock) Unlock() error {
	defer l.f.Close() // 其实不执行Close()也会释放目录锁
	return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN)
}

func main() {
	dir, _ := os.Getwd()
	wg := sync.WaitGroup{}

	// 5个协程,都尝试对目录加锁,加锁失败的就重试,加锁成功的2秒后释放
	for i := 0; i < 5; i++ {
		wg.Add(1)

		go func(num int) {
			dirLock := New(dir)
			ticker := time.NewTicker(time.Second) // 定时器每秒尝试1次
			for {
				select {
				case <-ticker.C:
					{
						err := dirLock.Lock() // 加锁尝试
						if err != nil {
							fmt.Printf("lock dir failed, goroutine num=%d, err=%s \n", num, err.Error())
							continue
						}
						fmt.Println("lock dir succeed, goroutine num=", num)
						goto end
					}
				}
			}

			end:
			time.Sleep(time.Second*2)
			dirLock.Unlock() // 解锁
			wg.Done()
		}(i)
	}
	wg.Wait()
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值