Golang并发操作中常见的读写锁详析

 更新时间:2021-09-22 22:53:18   作者:佚名   我要评论(0)

互斥锁简单粗暴,谁拿到谁操作。今天给大家介绍一下读写锁,读写锁比互斥锁略微复杂一些,不过我相信我们今天能够把他拿下!
golang读写锁,其

互斥锁简单粗暴,谁拿到谁操作。今天给大家介绍一下读写锁,读写锁比互斥锁略微复杂一些,不过我相信我们今天能够把他拿下!

golang读写锁,其特征在于

  • 读锁:可以同时进行多个协程读操作,不允许写操作
  • 写锁:只允许同时有一个协程进行写操作,不允许其他写操作和读操作

读写锁有两种模式。没错!一种是读模式,一种是写模式。当他为写模式的话,作用和互斥锁差不多,只允许有一个协程抢到这把锁,其他协程乖乖排队。但是读模式就不一样了,他允许你多个协程读,但是不能写。总结起来就是:

  • 仅读模式: 多协程可读不可写
  • 仅写模式: 单协程可写不可读

在32位的操作系统中,针对int64类型的值的读和写操作都不可能仅由一个CPU指令来完成。如若一个写操作刚刚执行完第一个指令,就去进行另一个读的协程,这样就会读到一个错误的数据。

下面看个例子吧:

先看主函数:

func main() {
    for i:=0;i<5;i++{
        wg06.Add(1)
        go write(i)
​
        wg06.Add(1)
        go read(i)
    }
    wg06.Wait()
}

每次开辟两条协程,一条协程执行写函数,另一条执行读函数。然后放入等待组。共开辟五次。

在来看一看写函数

func write(i int)  {
    //锁定为仅写模式,其他协程被阻塞
    rwm.Lock()
​
    fmt.Println(i,"writing...")
    <- time.After(10*time.Second)
    fmt.Println("write over!")
​
    rwm.Unlock()
    //解锁仅写模式
    wg06.Done()
}

这个Lock()就是执行读写锁的写模式,当这个模式进行时,只有这条协程能写,其他协程都被阻塞。Unlock()就是解锁这个仅锁模式,等待组中的其他协程不再被阻塞。

再看一看读模式:

func read(i int)  {
    rwm.RLock()
​
    fmt.Println(i,"reading...")
    <-time.After(10 * time.Second)
    fmt.Println(i,"read over!")
​
    rwm.RUnlock()
    wg06.Done()
}

RLock()就是执行读写锁的读模式,执行这个模式其他协程也能读,但是都不能写。

如果程序运行,写协程先抢到锁,所有协程就不能读,只有这条写协程能写,其他人都等着。如果是读协程抢到锁,所以写协程就不可能了,但是读协程仍然可以抢。

现在你知道我们应该什么时候使用读写锁了吗?

在并发进行读写操作时,当读的次数远远超过写的次数的情况下,应该使用读写锁来进行读写并发操作。

Golang读写锁底层原理

在加读锁和写锁的工程中都使用atomic.AddInt32来进行递增,而该指令在底层是会通过LOCK来进行CPU总线加锁的,因此多个CPU同时执行readerCount其实只会有一个成功,从这上面看其实是写锁与读锁之间是相对公平的,谁先达到谁先被CPU调度执行,进行LOCK锁cache line成功,谁就加成功锁

底层实现的CPU指令

底层的2条指令,通过LOCK指令配合CPU的MESI协议,实现可见性和内存屏障,同时通过XADDL则用来保证原子性,从而解决可见性与原子性问题

// atomic/asm_amd64.s TEXT runtime∕internal∕atomic·Xadd(SB)
    LOCK
    XADDL   AX, 0(BX)

可见性与内存屏障、原子性, 其中可见性通常是指在cpu多级缓存下如何保证缓存的一致性,即在一个CPU上修改了了某个数据在其他的CPU上不会继续读取旧的数据,内存屏障通常是为了CPU为了提高流水线性能,而对指令进行重排序而来,而原子性则是指的执行某个操作的过程的不可分割

总结

到此这篇关于Golang并发操作中常见读写锁的文章就介绍到这了,更多相关Golang并发读写锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang并发操作中常见的读写锁详析

    Golang并发操作中常见的读写锁详析

    互斥锁简单粗暴,谁拿到谁操作。今天给大家介绍一下读写锁,读写锁比互斥锁略微复杂一些,不过我相信我们今天能够把他拿下! golang读写锁,其
    2021-09-22
  • Go中Channel发送和接收操作指南

    Go中Channel发送和接收操作指南

    目录前言一、Channel的定义 二、Channel的操作 三、Channel发送和接收操作的特点 四、Channel的类型 五、Channel的源码学习 总结前言 先
    2021-09-22
  • golang 语言中错误处理机制

    golang 语言中错误处理机制

    与其他主流语言如 Javascript、Java 和 Python 相比,Golang 的错误处理方式可能和这些你熟悉的语言有所不同。所以才有了这个想法根大家聊一
    2021-09-22
  • 使用GO语言实现Mysql数据库CURD的简单示例

    使用GO语言实现Mysql数据库CURD的简单示例

    目录〇、介绍驱动包和增强版Mysql操作库Sqlx一、先导入驱动包和增强版Mysql操作库Sqlx 二、insert操作 三、delete操作 四、update操作 五
    2021-09-22
  • Go语言安装和GoLand2021最全超详细安装教程

    Go语言安装和GoLand2021最全超详细安装教程

    目录 前述一.Go语言的安装和下载。二.GoLand2021安装和下载参考教程 前述 Go语言和GoLand的关系好比于java和idea、python和pycharm,
    2021-09-22
  • 如何在VScode 中编译多个Go文件

    如何在VScode 中编译多个Go文件

    在使用VScode编译多个go文件多次遇到过一个问题:无法找到对应的函数。具体提示在好像是:在GOPATH和GOROOT路径中都没有找到该pack
    2021-09-22
  • Go 第三方库之类型转换问题

    Go 第三方库之类型转换问题

    目录01 为什么需要类型转换02 spf13/cast1)To_ 形式函数2)To_E 形式函数03 总结 强类型语言有它的优势,但也有不便利的地方,最典型的就是
    2021-09-22
  • Go Plugins插件的实现方式

    Go Plugins插件的实现方式

    官方实现 golang 1.8 及以上版本提供了一个创建共享库(shared object)的新工具,称为 Plugins。目前 Plugins 仅在 Linux、FreeBSD 和 macO
    2021-09-22
  • 基于HLS创建Golang视频流服务器的优缺点

    基于HLS创建Golang视频流服务器的优缺点

    HLS 是 HTTP Live Streaming 的缩写,是苹果开发的一种基于 HTTP 的自适应比特率流媒体传输协议, 并于 2009 年. HLS 流媒体已经成为应用最广
    2021-09-22
  • Go之集合slice的实现

    Go之集合slice的实现

    目录Slice(切片) 基于数组生成切片 切片修改 切片声明 Append 切片元素循环 Slice(切片) 切片和数组类似,可以把它理解为动态数
    2021-09-22

最新评论