golang下文件锁的使用[通俗易懂]

golang下文件锁的使用[通俗易懂]前言题目是golang下文件锁的使用,但本文的目的其实是通过golang下的文件锁的使用方法,来一窥文件锁背后的机制。为什么需要文件锁只有多线程/多进程这种并发场景下读写文件,才需要加锁,场景1-读写并发读写并发场景下,如果不加锁,就会出现读到脏数据的情况。想象一下,读文件的进程,读到第500字节,有其它进程以覆盖写的方式向文件中写入1000字节,那读进程读到的后500字节就是脏数据。场景2-写写并发写写并发场景下,如果不加锁,假设A进程先写0-1000字节,B进程写0-900字节,以此类

大家好,又见面了,我是你们的朋友全栈君。

前言

题目是golang下文件锁的使用,但本文的目的其实是通过golang下的文件锁的使用方法,来一窥文件锁背后的机制。

为什么需要文件锁

只有多线程/多进程这种并发场景下读写文件,才需要加锁,

场景1-读写并发

读写并发场景下,如果不加锁,就会出现读到脏数据的情况。想象一下,读文件的进程,读到第500字节,有其它进程以覆盖写的方式向文件中写入1000字节,那读进程读到的后500字节就是脏数据。

场景2-写写并发

写写并发场景下,如果不加锁,假设A进程先写0-1000字节,B进程写0-900字节,以此类推,最后一个进程写0-100字节,那最终的文件内容就是每个进程前100个字节拼接起来的错乱的内容了。

文件锁的几个概念

共享锁

共享锁,也叫读锁。某个进程首次获取共享锁后,会生成一个锁类型的变量L,类型标记为共享锁。其它进程获取读锁的时候,L中的计数器加1,表示又有一个进程获取到了共享锁。这个时候如果有进程来获取排它锁,会获取失败。

排它锁

排它锁,也叫写锁。某个进程首次获取排他锁后,会生成一个锁类型的变量L,类型标记为排他锁。其它进程获取任何类型的锁的时候,都会获取失败。

阻塞

阻塞的意思是说,新的进程发现当前的文件(数据)被加锁后,会一直处于等待状态,直到锁被释放,才会继续下一步的行为。

非阻塞

非阻塞的意思是说,新的进程发现当前的文件(数据)被加锁后,立即返回异常。业务上需要根据具体的业务场景对该异常进行处理。

阻塞和非阻塞其实是进程遇到锁的时候的两种处理模式。

golang下如何使用文件锁

基本使用


package main

import (
    "log"
    "os"
    "syscall"
)

func main() {
    f, err := os.Create("example.txt")
    if err != nil {
        log.Println("create file example.txt failed", err)
    }
    defer f.Close()
    // 非阻塞模式下,加共享锁
    if err := syscall.Flock(int(f.Fd()), syscall.LOCK_SH|syscall.LOCK_NB); err != nil {
        log.Println("add share lock in no block failed", err)
    }
    // 这里进行业务逻辑
    // TODO

    // 解锁
    if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil {
        log.Println("unlock share lock failed", err)
    }

    return
}

示例中 LOCK_SH 表示当前获取的是共享锁,如果是 LOCK_EX,则表示获取的是排他锁。而 LOCK_NB 表示当前获取锁的模式是非阻塞模式,如果需要阻塞模式,不加这个参数即可。LOCK_UN 则表示解锁,即释放锁。

golang 下这种文件锁的使用方式其实是Linux下的系统级调用,使用的是Linux的原生的文件锁的相关能力。

使用flock的几个注意点

1、只要fd指向的是同一个文件指针,那么加锁解锁的行为都是继承和覆盖的(这个可以看最后的解释)。

2、flock这种方式加的是建议性锁,也就是说新的进程一上来不管三七二十一,不去通过flock获取锁,就对文件各种操作,也是可以正常生效的。

说一说Linux下面的flock和fcntl

和flock一样,fcntl也是系统级调用,但是在具体的使用上却有很大不用,并且两种锁互不干扰,用flock加锁,fcntl无法感知,反之也一样。

建议性锁和强制锁

flock加的是建议性锁,而fcntl加的是强制性锁。

建议性锁,本质是一种协议,约定读写操作前都去检查一下该文件是否有被其它进程加锁。如果不遵守该协议,一上来就对文件进行操作,不检查有没有锁,程序执行上是没有任何问题的,能执行成功。

强制性锁,才更像真正意义上的锁。只要加了锁,其它进程是无法执行非允许的操作的。

其实一些利用redis做的分布式锁,都是建议性锁。锁机的机制要生效,需要大家共同遵守这个约定才行。

全局锁和局部锁

对于一个文件,flock加锁的范围是整个文件内容,而fcntl能对文件的任意部分加锁。

锁的持有者问题

flock认为,锁的持有者是文件表(可以理解为文件指针),所以对于fork和dup操作,他们都对应同一个文件指针,所有的操作都会作用到这个文件上。具体表现:

  • A进程加锁,A的子进程进程可以解锁,新的操作会覆盖之前的操作
  • A进程加锁,A进程复制fd,仍然是可以通过新的fd操作文件锁,新的操作会覆盖之前的操作

fcntl 认为,锁的持有者是进程。加锁和解锁的行为都是跟着进程走,具体表现为:

  • A进程加锁,B进程得等A进程消亡或者解锁才能加锁

参考

[1] 被遗忘的桃源——flock 文件锁

[2] Linux文件锁学习-flock, lockf, fcntl

原创不易,欢迎关注我的公众号:码农的自由之路

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/153498.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月28日 下午4:46
下一篇 2022年6月28日 下午5:00


相关推荐

  • SCI论文投稿信(Cover Letter)的写法及模版

    SCI论文投稿信(Cover Letter)的写法及模版一、写法投稿信(CoverLetter)应该简述所投稿件的核心内容、主要发现和意义,拟投期刊,对稿件处理有无特殊要求等(如“nottoreview”list)。另外,请附上主要作者的中文姓名、通讯地址、电话、传真和e-mail地址。此外有的杂志要求推荐几位审稿人及其联系方式。以及谁已经阅读过该文(当然是牛人)。一般来说,杂志通常要求说明你论文研究的意义,以及与这个杂志的相关性,另外还有…

    2022年5月8日
    132
  • PCL 使用GICP对点云配准

    PCL 使用GICP对点云配准面对面的 ICP 算法

    2026年3月18日
    4
  • 敏捷开发流程总结

    敏捷开发流程总结

    2021年12月1日
    53
  • 2019latex安装教程

    2019latex安装教程菜鸡一枚,一开始装latex找了好多博客,按照他们的步骤走还是失败,最后把他们集齐,拼在了一起,终于召唤出了latex!!!参考博客链接:安装教程环境变量教程安装教程环境变量教程其实还找了许多,但是最上面的四个教程是最有用的(对我来说)首先去这个链接下latex官网具体操作步骤看这个安装教程,他写的很好很详细,在按照步骤装好texlive后,先别急!,要更改环境变量(我是需要的…

    2022年6月9日
    52
  • EasyAdmin 注解权限

    EasyAdmin 注解权限控制器注解权限控制器类注解 tag ControllerAn 注解类 EasyAdmin annotation ControllerAn 作用范围 CLASS 参数说明 title 控制器的名称 必填 auth 是否开启权限控制 默认为 true 选填 Enum true false 示例备注 注解前请先引用 useEasyAdmin annotation ControllerAn phpnam

    2026年3月19日
    2
  • Cooley-Tukey算法 (蝶形算法)

    Cooley-Tukey算法 (蝶形算法)

    2021年12月2日
    71

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号