go源码阅读 - sync/rwmutex
相比于Mutex来说,RWMutex锁的粒度更细,使用RWMutex可以并发读,但是不能并发读写,或者写写。
1. sync.RWMutex的结构
type RWMutex struct {
        // 互斥锁
	w           Mutex  // held if there are pending writers
        // 信号量,用于写等待读
	writerSem   uint32 // semaphore for writers to wait for completing readers
        // 信号量,用于读等待写
	readerSem   uint32 // semaphore for readers to wait for completing writers
        // 当前执行读的goroutine的数量
	readerCount int32  // number of pending readers
        // 获取写锁时需要等待的读锁释放数量
	readerWait  int32  // number of departing readers
}
const rwmutexMaxReaders = 1 << 30
2. 加读锁 RLock()
// RLock locks rw for reading.
//
// It should not be used for recursive read locking; a blocked Lock
// call excludes new readers from acquiring the lock. See the
// documentation on the RWMutex type.
func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// 调用这个原子方法,原子加 1
// 如果写锁已经被获取,那么 readercount在 -rwmutexMaxReaders和0之间,这是将请求获取 RLock的 goroutine的挂起
// 如果写锁没有被获取,那么 readercount大于 0,获取读锁 ,不阻塞。
// 通过 readerCount判断读锁与写锁互斥,如果存在写锁则挂起goroutine,多个读锁可以并行
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
// 这时休眠这个goroutine,等待被唤醒
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
3. 解读锁
// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
if race.Enabled {
_ = rw.w.state
race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
race.Disable()
}
// 正在读的goroutine数减1
// 如果小于0,说明 1. 当前有正在等待获取写锁的goroutine或者多次解锁,进入慢速通道
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
// Outlined slow-path to allow the fast-path to be inlined
rw.rUnlockSlow(r)
}
if race.Enabled {
race.Enable()
}
}
func (rw *RWMutex) rUnlockSlow(r int32) {
   // 如果多次解锁,则抛出异常
   if r+1 == 0 || r+1 == -rwmutexMaxReaders {
      race.Enable()
      throw("sync: RUnlock of unlocked RWMutex")
   }
   // A writer is pending.
   // 如果有正在等待获取写锁的goroutine, 并且当前的read goroutine是最后一个持有读锁的goroutine, 那么通过信号量唤醒等待获取写锁的goroutine.
   if atomic.AddInt32(&rw.readerWait, -1) == 0 {
      // The last reader unblocks the writer.
      runtime_Semrelease(&rw.writerSem, false, 1)
   }
}
4. 加写锁
// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// First, resolve competition with other writers.
// 首先调用互斥锁的 Lock,获取到互斥锁
rw.w.Lock()
// Announce to readers there is a pending writer.
// 阻塞后续的读操作
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
// 如果计算之后仍有其他goroutine获取读锁,,那么调用 runtime_SemacquireMutex 休眠当前的goroutine直到所有的读操作完成
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
}
}
5. 解写锁
// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock.
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
if race.Enabled {
_ = rw.w.state
race.Release(unsafe.Pointer(&rw.readerSem))
race.Disable()
} // Announce to readers there is no active writer.
// 将 readerCount 恢复
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
// 循环唤醒等待获取读锁的 goroutine
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
// Allow other writers to proceed.
rw.w.Unlock()
if race.Enabled {
race.Enable()
}
}
go源码阅读 - sync/rwmutex的更多相关文章
- go源码阅读 - sync/mutex
		
Mutex是go标准库中的互斥锁,用于处理并发场景下共享资源的访问冲突问题. 1. Mutex定义: // A Mutex is a mutual exclusion lock. // The zer ...
 - 10 DelayQueue 延时队列类——Live555源码阅读(一)基本组件类
		
这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 www.cnblogs.com/oloroso/ 本文由乌合 ...
 - Netty源码阅读(一) ServerBootstrap启动
		
Netty源码阅读(一) ServerBootstrap启动 转自我的Github Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速 ...
 - 【 js 基础 】【 源码学习 】backbone 源码阅读(一)
		
最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...
 - 【 js 基础 】【 源码学习 】backbone 源码阅读(三)浅谈 REST 和 CRUD
		
最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...
 - InfluxDB源码阅读之snapshotter服务
		
操作系统 : CentOS7.3.1611_x64 go语言版本:1.8.3 linux/amd64 InfluxDB版本:1.1.0 服务模块介绍 源码路径: github.com/influxda ...
 - Apollo源码阅读笔记(二)
		
Apollo源码阅读笔记(二) 前面 分析了apollo配置设置到Spring的environment的过程,此文继续PropertySourcesProcessor.postProcessBeanF ...
 - 【 js 基础 】【 源码学习 】backbone 源码阅读(三)
		
最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...
 - Redis源码阅读(二)高可用设计——复制
		
Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致 ...
 
随机推荐
- 分布式 PostgreSQL 集群(Citus),官方快速入门教程
			
多租户应用程序 在本教程中,我们将使用示例广告分析数据集来演示如何使用 Citus 来支持您的多租户应用程序. 注意 本教程假设您已经安装并运行了 Citus. 如果您没有运行 Citus,则可以使用 ...
 - 4月25日 python学习总结 互斥锁 IPC通信 和 生产者消费者模型
			
一.守护进程 import random import time from multiprocessing import Process def task(): print('name: egon') ...
 - 同一局域网ping ip 失败(可能是你的路由器坑了你)
			
事件起源:手机需要通过fiddler代理抓包 一顿操作猛如虎,手机输入ip+端口,芭比q了,连接不上.. 解决思路: 一.保证双方处于同一局域网内 二.查看电脑配置,找到 防火墙,关闭防火墙,再试 三 ...
 - Python GUI tkinter 学习笔记(三)
			
草稿 # -*- coding: utf-8 -*- from Tkinter import * root = Tk() Label(root, text = "First").g ...
 - weblogic重要漏洞记录
			
(PS:之前在freebuf发过,这里直接复制过来的,所以有些图片会有水印) 前言 T3协议存在多个反序列化漏洞CVE-2015-4852/CVE-2016-0638/CVE-2016-3510/CV ...
 - strcpy、strncpy 和安全的strncpy_s
			
strcpy和strncpy摘于linux 内核源码的/lib/string.c char *self_strcpy(char *dest, const char *src) { char *tmp ...
 - Java的重载以及与重写的区别
			
一.什么是方法重载 方法的重载就是在同一个类中,有着若干个名字相同的方法.在具体调用这些方法的时候,通过传递参数的不同来调用这些重载方法. 二.为什么需要方法重载 方法名的定义需要做到见名知意,功能类 ...
 - ActiveMQ数据接收类型问题
			
一.问题描述 最近开发了一个工具,功能是监听ActiveMQ消息然后做相应的处理,本地自测没有问题,但是部署在现场出现如下报错: [WARN ] [2020-08-27 19:49:42] [org. ...
 - Oracle入门基础(二)一一过滤和排序
			
SQL> --查询10号部门的员工 SQL> select * from emp where deptno=10; EMPNO ENAME JOB MGR HIREDATE SAL COM ...
 - jpa CriteriaQueryNo explicit selection and an implicit one could not be determined
			
java.lang.IllegalArgumentException: Error occurred validating the Criteria ... Caused by: java.lang. ...