go sync包(四) 读写锁(二)

读写锁 RWMutex

写锁

加锁

RWMetex 的写锁复用了 Mutex

// 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.staterace.Disable()}// First, resolve competition with other writers.// writer加锁rw.w.Lock()// Announce to readers there is a pending writer.// 反转 readerCount,告诉 reader 有writer竞争锁// Add(-rwmutexMaxReaders) 将 readerCount 置为负数,表示当前有 writer 竞争锁r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.// 当前有 reader 持有锁,writer 等待if r != 0 && rw.readerWait.Add(r) != 0 {runtime_SemacquireRWMutex(&rw.writerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))race.Acquire(unsafe.Pointer(&rw.writerSem))}
}
解锁

解锁也是复用的 Mutex,区别在于解锁的时候会唤醒所有阻塞的 reader

// 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.staterace.Release(unsafe.Pointer(&rw.readerSem))race.Disable()}// Announce to readers there is no active writer.// 反转 readerCount,告诉 reader 没有 writer 竞争锁r := rw.readerCount.Add(rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()fatal("sync: Unlock of unlocked RWMutex")}// Unblock blocked readers, if any.// 唤醒所有阻塞的 readerfor i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// Allow other writers to proceed.// writer 解锁rw.w.Unlock()if race.Enabled {race.Enable()}
}

读锁

加锁
// 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.staterace.Disable()}// writer 加锁时,readerCount 是负数// 写锁优先级比读锁高,reader 休眠// 如果 reader+1 > 0, 当前加了读锁if rw.readerCount.Add(1) < 0 {// A writer is pending, wait for it.runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))}
}
解锁
// 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.staterace.ReleaseMerge(unsafe.Pointer(&rw.writerSem))race.Disable()}// 有等待的 writerif r := rw.readerCount.Add(-1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}if race.Enabled {race.Enable()}
}func (rw *RWMutex) rUnlockSlow(r int32) {if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()fatal("sync: RUnlock of unlocked RWMutex")}// A writer is pending.// 读锁全部解锁完毕,唤醒 writerif rw.readerWait.Add(-1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)}
}

小结

  • sync.RWMutex 复用 sync.Mutex
  • 写锁:
    • 加锁时 writer 反转 readerCount 置为负数,如果当前有 reader 正在执行, readerCount 赋值给 readerWait,writer 休眠。
    • 解锁时 writer 反转 readerCount 置为正常,并唤醒所有阻塞的 reader。
  • 读锁:
    • 加锁时 readerCount + 1,如果 readerCount < 0,reader 阻塞。
    • 解锁时 readerCount - 1,如果 readerCount < 0,readerWait - 1,当 readerWait == 0 时,唤醒 writer。

sync.RWMutex 复用 sync.Mutex

  • 调用 sync.RWMutex.Lock 尝试获取写锁时;
  • 每次 sync.RWMutex.RUnlock 都会将 readerCount 其减一,当它归零时该 Goroutine 会获得写锁;
  • 将 readerCount 减少 rwmutexMaxReaders 个数以阻塞后续的读操作;

调用 sync.RWMutex.Unlock 释放写锁时,会先通知所有的读操作,然后才会释放持有的互斥锁;

读写互斥锁在互斥锁之上提供了额外的更细粒度的控制,能够在读操作远远多于写操作时提升性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/36898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安全与发展并重:实施等保,促进企业可持续增长的逻辑

在数字经济时代&#xff0c;信息安全不仅是企业稳健运营的基石&#xff0c;也是推动可持续发展的重要保障。网络安全等级保护&#xff08;简称“等保”&#xff09;体系&#xff0c;作为国家层面设立的信息安全保障框架&#xff0c;其核心在于平衡安全与发展的关系&#xff0c;…

Java中如何进行分布式系统设计?

Java中如何进行分布式系统设计&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;我们来讨论如何在Java中进行分布式系统设计。分布式…

什么是 Python 包管理器?怎么安装?

Python 包管理器是一个用于安装、升级、卸载和管理 Python 包的工具。Python 的包&#xff08;也称为模块或库&#xff09;是预编写的 Python 代码&#xff0c;用于执行各种任务&#xff0c;如数据处理、网页开发、科学计算等。Python 包管理器使得这些包的管理变得简单和高效。…

Android Gradle开发与应用 (第一部分):入门Gradle基础

Gradle 是一个开源的构建自动化工具&#xff0c;广泛用于Android项目的构建和管理。本文将介绍Gradle的基础知识&#xff0c;帮助开发者更好地理解和使用Gradle进行Android应用开发。 目录 什么是GradleGradle的基本概念配置Gradle环境Gradle构建脚本结构常用Gradle命令多项目…

计算Dice损失的函数

计算Dice损失的函数 def Dice_loss(inputs, target, beta1, smooth 1e-5):n,c, h, w inputs.size() #nt,ht, wt, ct target.size() #nt,if h ! ht and w ! wt:inputs F.interpolate(inputs, size(ht, wt), mode"bilinear", align_cornersTrue)temp_inputs t…

LLaMA-Factory安装

安装代码 https://github.com/echonoshy/cgft-llm/blob/master/llama-factory/README.md https://github.com/hiyouga/LLaMA-Factory/tree/mainLLaMA-Factoryhttps://github.com/hiyouga/LLaMA-Factory/tree/main 【大模型微调】- 使用Llama Factory实现中文llama3微调_哔哩…

TIA博途WinCC通过VB脚本从 Excel中读取数据的具体方法介绍

TIA博途WinCC通过VB脚本从 Excel中读取数据的具体方法介绍 添加 一个PLC,设置PLC的IP地址,如下图所示, 添加全局DB块,新建几个变量,如下图所示, 在数据块中添加了 tag1 …… tag6 ,共 6 个浮点数类型的变量,用来接收通过 WinCC 从 Excel 文件中读取的数据。 添加 HMI…

Holt-Winters季节性方法

Holt-Winters季节性方法是时间序列预测中一种常用的方法&#xff0c;它通过三次指数平滑处理数据中的趋势和季节性成分。下面将详细解释该方法的原理和步骤&#xff1a; 1. 数据准备 数据收集与整理&#xff1a;首先需要收集和整理时间序列数据&#xff0c;确保数据的准确性和…

什么是pip命令

pip 是 Python 的包管理器&#xff0c;用于安装和管理 Python 包&#xff08;也称为模块或库&#xff09;。Python 包是预编写的 Python 代码&#xff0c;用于执行特定任务&#xff0c;如数据处理、网页开发、科学计算等。通过使用 pip&#xff0c;您可以轻松地安装、升级或卸载…

iOS 语言基础初探 Xcode 工具

iOS&#xff08;iPhone 操作系统&#xff09;是由苹果公司开发的移动操作系统&#xff0c;运行在iPhone、iPad和iPod Touch等设备上。苹果官方推荐使用Objective-C和Swift这两种编程语言来开发iOS应用程序。 Objective-C是一种面向对象的编程语言&#xff0c;也是iOS开发的主要…

gdb-dashboard:用Python重塑GDB调试体验

gdb-dashboard&#xff1b;一目了然的GDB调试&#xff0c;尽在掌控之中- 精选真开源&#xff0c;释放新价值。 概览 gdb-dashboard是一个用Python编写的模块化视觉界面&#xff0c;为GNU Debugger&#xff08;GDB&#xff09;提供了一个现代化的工作空间。它通过集成多个面板和…

数据平台发展史-从数据仓库数据湖到数据湖仓

做数据的同学经常听到一些数据相关的术语&#xff0c;常见的包括数据仓库&#xff0c;逻辑数据仓库&#xff0c;数据湖&#xff0c;数据湖仓/湖仓一体&#xff0c;数据网格 data mesh,数据编织 data fabric等. 笔者在这里回顾了下数据平台的发展史&#xff0c;也介绍和对比了下…

【QT】按钮类控件 显示类控件

目录 按钮类控件 Push Button 设置按钮图标 按钮设置快捷键 设置鼠标点击按钮重复触发 Radio Button 单选框分组 Check Box 显示类控件 Label 常用属性 设置文本格式 给Label设置图片 Label标签设置边框 设置文本对齐方式 设置文本自动换行 设置文本缩进 设置…

Python入门-基本数据类型-字符串类型及其操作

字符串类型存储的数据是字符串&#xff0c;字符串是一个由字符构成的序列。Python字符串是不可变的 不支持动态修改。本节将对字符串进行简单介绍&#xff0c;包括字符串的定义方式、格式化、索引、切片 拼接、重复和成员归属等。 1.字符串的定义方式 1.1单行字符串 单行字符…

《概率论与数理统计》期末笔记_下

目录 第4章 随机变量的数字特征 4.1 数学期望 4.2 方差 4.3 常见分布的期望与方差 4.4 协方差与相关系教 第5章 大数定律和中心极限定理 5.1 大数定律 5.2 中心极限定理 第6章 样本与抽样分布 6.1 数理统汁的基本概念 6.2 抽样分布 6.2.1 卡方分布 6.2.2 t分布 6.…

高效利用iCloud的指南

高效利用iCloud的指南可以帮助你充分利用Apple提供的云服务&#xff0c;以下是一些关键的步骤和建议&#xff1a; 了解iCloud的基本功能 iCloud是Apple提供的云服务&#xff0c;可以安全地存储你的个人信息&#xff0c;包括照片、文件、备忘录、日历、联系人等。通过iCloud&…

JavaScript(4)——数组,常量

let和var的区别 var可以先使用再声明var声明过的变量可以重复声明 数组 声明语法&#xff1a; let 数组名 [数据1&#xff0c;数据2&#xff0c;数据3,...] 数组是按顺序保存&#xff0c;所以每个数据都有自己的编号计算机中的编号从0开始编号也叫索引或下标 数组可以存储任…

java基于ssm+jsp 高校毕业生就业满意度调查统计系统

1用户前台功能模块 高校毕业生就业满意度调查统计系统&#xff0c;在高校毕业生就业满意度调查统计系统可以查看首页、问卷、就业咨询、试卷列表、新闻资讯、留言反馈、我的、跳转到后台等内容&#xff0c;如图1所示。 图1系统首页界面图 用户登录、用户注册&#xff0c;通过…

LabVIEW代码性能优化

优化LabVIEW代码以提高软件性能是确保系统高效运行的关键。通过分析代码结构、数据管理、并行处理、内存使用和硬件资源的有效利用&#xff0c;我们可以从多个角度提升LabVIEW程序的执行速度和稳定性。 代码结构优化 模块化编程 将复杂的程序分解成多个子VI&#xff0c;每个子V…

Linux-进程间通信(IPC)

进程间通信&#xff08;IPC&#xff09;介绍 进程间通信&#xff08;IPC&#xff0c;InterProcess Communication&#xff09;是指在不同的进程之间传播或交换信息。IPC 的方式包括管道&#xff08;无名管道和命名管道&#xff09;、消息队列、信号量、共享内存、Socket、Stre…