RWMutex:是基于Mutex实现的读写互斥锁,一个goroutine可以持有多个读锁或者一个写锁,同一时刻只能持有读锁或者写锁

数据结构设计:

type RWMutex struct {
w Mutex // 互斥锁
writerSem uint32 // 写锁信号量
readerSem uint32 // 读锁信号量
readerCount int32 // 读锁计数器
readerWait int32 // 获取写锁时需要等待的读锁释放数量
}
// 获取写锁
func (rw *RWMutex) Lock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// 先获取一把互斥锁
rw.w.Lock()
// 减去最大的读锁数量,用0-负数来表示写锁已经被获取
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 设置需要等待释放的读锁数量,如果有,则挂起获取读锁的goroutine
if r != && atomic.AddInt32(&rw.readerWait, r) != {
// 挂起,监控写锁信号量
runtime_Semacquire(&rw.writerSem)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
}
}

按顺序这里应该介绍释放写锁的代码了,但是由于获取写锁中有很重要的几个逻辑变量,跟获取读锁时强依赖,所以在这里先说说获取读锁的逻辑

// 获取读锁
func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
} // 每次获取读锁时,readerCount+1
// 如果写锁已经被获取,那么readerCount在-rwmutexMaxReaders与0之间,这时挂起获取读锁的goroutine,
// 如果写锁没有被获取,那么readerCount>=0,然后就没然后了
// 这样通过readerCount的正负就成了读锁与写锁互斥的判断条件 if atomic.AddInt32(&rw.readerCount, ) < {
// 挂起,监听readerSem信号量
runtime_Semacquire(&rw.readerSem)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
// 释放读锁
func (rw *RWMutex) RUnlock() {
if race.Enabled {
_ = rw.w.state
race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
race.Disable()
}
// 读锁计数器-1
if r := atomic.AddInt32(&rw.readerCount, -); r < {
if r+ == || r+ == -rwmutexMaxReaders {
race.Enable()
panic("sync: RUnlock of unlocked RWMutex")
}
// 如果获取写锁时的goroutine被阻塞,这时需要获取读锁的goroutine全部都释放,才会被唤醒
if atomic.AddInt32(&rw.readerWait, -) == { // 更新需要释放的读锁数量
// 更新信号量
runtime_Semrelease(&rw.writerSem)
}
}
if race.Enabled {
race.Enable()
}
}
func (rw *RWMutex) Unlock() {
if race.Enabled {
_ = rw.w.state
race.Release(unsafe.Pointer(&rw.readerSem))
race.Release(unsafe.Pointer(&rw.writerSem))
race.Disable()
} // 还原加锁时减去的那一部分readerCount
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
panic("sync: Unlock of unlocked RWMutex")
}
// 唤醒获取读锁期间所有被阻塞的goroutine
for i := ; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem)
}
// 释放互斥锁资源
rw.w.Unlock()
if race.Enabled {
race.Enable()
}
}

总结:

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

1. 读锁不能阻塞读锁,引入readerCount实现

2. 读锁需要阻塞写锁,直到所以读锁都释放,引入readerSem实现

3. 写锁需要阻塞读锁,直到所以写锁都释放,引入wirterSem实现

4. 写锁需要阻塞写锁,引入Metux实现

golang RWMutex读写锁分析的更多相关文章

  1. golang map 读写锁与深度拷贝的坑

    0X01 golang中,map(字典)无法并发读写 简单来说,新建万条线程对同一个map又读又写,会报错. 为此,最好加锁,其实性能影响并不明显. type taskCache struct{ sy ...

  2. Golang 读写锁RWMutex 互斥锁Mutex 源码详解

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...

  3. go Mutex (互斥锁)和RWMutex(读写锁)

    转载自: https://blog.csdn.net/skh2015java/article/details/60334437 golang中sync包实现了两种锁Mutex (互斥锁)和RWMute ...

  4. golang 互斥锁和读写锁

    golang 互斥锁和读写锁 golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. ty ...

  5. Go基础系列:互斥锁Mutex和读写锁RWMutex用法详述

    sync.Mutex Go中使用sync.Mutex类型实现mutex(排他锁.互斥锁).在源代码的sync/mutex.go文件中,有如下定义: // A Mutex is a mutual exc ...

  6. golang互斥锁和读写锁

    一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开 ...

  7. Golang 入门系列(十六)锁的使用场景主要涉及到哪些?读写锁为什么会比普通锁快

    前面已经讲过很多Golang系列知识,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html, 接下来要说的 ...

  8. Java 并发包中的读写锁及其实现分析

    1. 前言 在Java并发包中常用的锁(如:ReentrantLock),基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时 刻可以允许多个读线程访问,但是在写线程访问时,所有 ...

  9. [源码分析]读写锁ReentrantReadWriteLock

    一.简介 读写锁. 读锁之间是共享的. 写锁是独占的. 首先声明一点: 我在分析源码的时候, 把jdk源码复制出来进行中文的注释, 有时还进行编译调试什么的, 为了避免和jdk原生的类混淆, 我在类前 ...

随机推荐

  1. [原创]Hadoop-2.5.2-HA原文译

    使用the Quorum Journal Manager实现HDFS高可用 2017/1/22 11:57:22 原文 目的(Purpose) * 这个指南提供了对HDFS-HA特性,使用QJM特性如 ...

  2. 从新手到高手c++全方位学习 pdf + 视频教程 共18章

    淘宝已经和谐了这个网站,原网址:https://item.taobao.com/item.htm?spm=a1z09.8149145.0.0.mb00D0&id=17350311256& ...

  3. Windows 10 IoT Serials 5 - 如何为树莓派应用程序添加语音识别与交互功能

    都说语音是人机交互的重要手段,虽然个人觉得在大庭广众之下,对着手机发号施令会显得有些尴尬.但是在资源受限的物联网应用场景下(无法外接鼠标键盘显示器),如果能够通过语音来控制设备,与设备进行交互,那还是 ...

  4. 如何用JS/HTML将时间戳转换为“xx天前”的形式【附源码,转

    如果我们有一份过去时间戳,如何使用JS/HTML将时间戳转换为"xx天前"的形式呢,以下是完整代码 <!DOCTYPE html> <html> <h ...

  5. 如何在Oracle中复制表结构和表数据 【转载】

    1. 复制表结构及其数据: create table table_name_new as select * from table_name_old 2. 只复制表结构: create table ta ...

  6. WinForm——记住密码

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Runti ...

  7. Import Statements 导入语句

    Syntax of an Import Statement 导入语句的语法 An import statement allows clients to tell the engine which mo ...

  8. Python学习--20 Web开发

    HTTP格式 HTTP协议是基于TCP和IP协议的.HTTP协议是一种文本协议. 每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的. HTTP ...

  9. iphone在iframe页面的宽度不受父页面影响,避免撑开页面

    工作中有个需求,就是产品页面通过iframe引用显示产品协议页,要求不要横向滑动,只需要竖向滑动,但在iphone中引用的iframe会撑开父页的宽度,而在android端浏览器这不会. <di ...

  10. HTML学习二

    继续上一次的学习: <html> <head> <title>新增雇员</title> <script language="javasc ...