详解golang RWMutex读写互斥锁源码分析

针对Golang 1.9的sync.RWMutex进行分析,与Golang 1.10基本一样除了将panic改为了throw之外其他的都一样。

RWMutex是读写互斥锁。锁可以由任意数量的读取器或单个写入器来保持。

RWMutex的零值是一个解锁的互斥锁。

以下代码均去除race竞态检测代码

源代码位置:sync\rwmutex.go

结构体

type RWMutex struct {
  w      Mutex // 互斥锁
  writerSem  uint32 // 写锁信号量
  readerSem  uint32 // 读锁信号量
  readerCount int32 // 读锁计数器
  readerWait int32 // 获取写锁时需要等待的读锁释放数量
}

常量

const rwmutexMaxReaders = 1 << 30  // 支持最多2^30个读锁

方法

Lock

提供写锁操作.

func (rw *RWMutex) Lock() {
  // 竞态检测
 if race.Enabled {
 _ = rw.w.state
 race.Disable()
 }
 // 使用Mutex锁
 rw.w.Lock()
 // Announce to readers there is a pending writer.
 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
 // Wait for active readers.
 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
 runtime_Semacquire(&rw.writerSem)
 }
 // 竞态检测
 if race.Enabled {
 race.Enable()
 race.Acquire(unsafe.Pointer(&rw.readerSem))
 race.Acquire(unsafe.Pointer(&rw.writerSem))
 }
}

RLock

提供读锁操作,

func (rw *RWMutex) RLock() {
  // 竞态检测
 if race.Enabled {
 _ = rw.w.state
 race.Disable()
 }
 // 每次goroutine获取读锁时,readerCount+1
  // 如果写锁已经被获取,那么readerCount在-rwmutexMaxReaders与0之间,这时挂起获取读锁的goroutine,
  // 如果写锁没有被获取,那么readerCount>0,获取读锁,不阻塞
  // 通过readerCount判断读锁与写锁互斥,如果有写锁存在就挂起goroutine,多个读锁可以并行
 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
 // 将goroutine排到G队列的后面,挂起goroutine
 runtime_Semacquire(&rw.readerSem)
 }
 // 竞态检测
 if race.Enabled {
 race.Enable()
 race.Acquire(unsafe.Pointer(&rw.readerSem))
 }
}

RLocker

可以看到RWMutex实现接口Locker.

type Locker interface {
 Lock()
 Unlock()
}

而方法RLocker就是将RWMutex转换为Locker.

func (rw *RWMutex) RLocker() Locker {
 return (*rlocker)(rw)
}

总结

读写互斥锁的实现比较有技巧性一些,需要几点

  1. 读锁不能阻塞读锁,引入readerCount实现
  2. 读锁需要阻塞写锁,直到所以读锁都释放,引入readerSem实现
  3. 写锁需要阻塞读锁,直到所以写锁都释放,引入wirterSem实现
  4. 写锁需要阻塞写锁,引入Metux实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Golang互斥锁内部实现

    go语言提供了一种开箱即用的共享资源的方式,互斥锁(sync.Mutex), sync.Mutex的零值表示一个没有被锁的,可以直接使用的,一个goroutine获得互斥锁后其他的goroutine只能等到这个gorutine释放该互斥锁,在Mutex结构中只公开了两个函数,分别是Lock和Unlock,在使用互斥锁的时候非常简单,本文并不阐述使用. 在使用sync.Mutex的时候千万不要做值拷贝,因为这样可能会导致锁失效.当我们打开我们的IDE时候跳到我们的sync.Mutex 代码中会发现

  • GO语言并发编程之互斥锁、读写锁详解

    在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和Unlock.顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁. 类型sy

  • Go语言实现互斥锁、随机数、time、List

    Go语言实现互斥锁.随机数.time.List import ( "container/list" "fmt" "math/rand" //备注2:随机数的包 "sync" //备注1:异步任务的包 "time" ) type INFO struct { lock sync.Mutex //备注1:异步锁 Name string Time int64 } var List *list.List = list

  • 详解golang RWMutex读写互斥锁源码分析

    针对Golang 1.9的sync.RWMutex进行分析,与Golang 1.10基本一样除了将panic改为了throw之外其他的都一样. RWMutex是读写互斥锁.锁可以由任意数量的读取器或单个写入器来保持. RWMutex的零值是一个解锁的互斥锁. 以下代码均去除race竞态检测代码 源代码位置:sync\rwmutex.go 结构体 type RWMutex struct { w Mutex // 互斥锁 writerSem uint32 // 写锁信号量 readerSem uin

  • Golang Mutex互斥锁源码分析

    目录 前言 Mutex 特性 数据结构 Lock() Unlock() 前言 在上一篇文章中,我们一起学习了如何使用 Go 中的互斥锁 Mutex,那么本篇文章,我们就一起来探究下 Mutex 底层是如何实现的,知其然,更要知其所以然! 说明:本文中的示例,均是基于Go1.17 64位机器 Mutex 特性 Mutex 就是一把互斥锁,可以想象成一个令牌,有且只有这一个令牌,只有持有令牌的 goroutine 才能进入房间(临界区),在房间内执行完任务后,走出房间并把令牌交出来,如果还有其余的 

  • golang并发安全及读写互斥锁的示例分析

    目录 并发安全和锁 互斥锁 读写互斥锁 并发安全和锁 有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态).类比现实生活中的例子有十字路口被各个方向的的汽车竞争:还有火车上的卫生间被车厢里的人竞争. 举个例子: var x int64 var wg sync.WaitGroup func add() { for i := 0; i < 5000; i++ { x = x + 1 } wg.Done() } func main() { w

  • 详解查看JAVA API及JAVA源码的方法

    在java的日常学习中,我们有时候会需要看java的api说明,或者是查看java的源码,使我们更好的了解java,接下来我就来说说如何查看java的api以及java源码 对于java的api,一般是在下面的网址中进行查看 https://docs.oracle.com/javase/8/docs/api/ 而对于java的源码,我们现在来演示查看nextLine()的源码: 将鼠标放置在希望转跳到源码的函数上,等待系统浮现这个黄色的框 然后点击下面的Open Declaration,即可进入

  • 详解C++11的std::addressof源码解析

    目录 1.源码准备 2.std::addressof简介 3.std::addressof源码解析 4.总结 1.源码准备 本文是基于gcc-4.9.0的源代码进行分析,std::addressof是C++11才加入标准的,所以低版本的gcc源码是没有这个的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的,下面给出源码下载地址 http://ftp.gnu.org/gnu/gcc 2.std::addressof简介 std::addresso

  • MySQL InnoDB 事务锁源码分析

    目录 1. Lock 与 Latch 2. Repeatable Read 3. Insert加锁流程 3.1 lock mode 3.2 加锁流程 3.3 隐式锁 4. Select 加锁流程 本文前提: 代码MySQL 8.0.13 只整理Repeatable Read当前读.Read Committed简单很多,另外快照读是基于MVCC不用加锁,所以不在本文讨论范畴. 1. Lock 与 Latch InnoDB 中的lock是事务中对访问/修改的record加的锁,它一般是在事务提交或回

  • 详解java中的互斥锁信号量和多线程等待机制

    互斥锁和信号量都是操作系统中为并发编程设计基本概念,互斥锁和信号量的概念上的不同在于,对于同一个资源,互斥锁只有0和1 的概念,而信号量不止于此.也就是说,信号量可以使资源同时被多个线程访问,而互斥锁同时只能被一个线程访问 互斥锁在java中的实现就是 ReetranLock , 在访问一个同步资源时,它的对象需要通过方法 tryLock() 获得这个锁,如果失败,返回 false,成功返回true.根据返回的信息来判断是否要访问这个被同步的资源.看下面的例子 public class Reen

  • 详解SpringBoot集成jsp(附源码)+遇到的坑

    本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置application.properties支持jsp (4)编写测试Controller (5)编写JSP页面 (6)编写启动类App.java 2.新建SpringInitialzr 3.pom文件 <dependencies> <dependency> <groupId>org.s

  • 详解python3 GUI刷屏器(附源码)

    过年GUI博客二连发,本打算出去玩玩,奈何空气,天气实在差,遂使用tkinter开发一款GUI刷屏器,写此博客记录一下我的开发思路. 一.准备工作 本次使用除tkinter库之外还使用了pynput库,可以使用 pip install pynput 安装 二.预览 在长文本框中输入要刷屏的内容,通过设置刷屏频率(单位:秒)即可实现刷屏. 三.设计流程 四.源代码 import re import time import pyperclip from tkinter import * from t

  • 详解Golang中select的使用与源码分析

    目录 背景 select 流程 背景 golang 中主推 channel 通信.单个 channel 的通信可以通过一个goroutine往 channel 发数据,另外一个从channel取数据进行.这是阻塞的,因为要想顺利执行完这个步骤,需要 channel 准备好才行,准备好的条件如下: 1.发送 缓存有空间(如果是有缓存的 channel) 有等待接收的 goroutine 2.接收 缓存有数据(如果是有缓存的 channel) 有等待发送的 goroutine 对channel实际使

随机推荐