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


Posted in Golang onAugust 30, 2021

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

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 相关文章推荐
goland 恢复已更改文件的操作
Apr 28 Golang
解决golang在import自己的包报错的问题
Apr 29 Golang
解决golang结构体tag编译错误的问题
May 02 Golang
聊聊golang中多个defer的执行顺序
May 08 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
手把手教你导入Go语言第三方库
Aug 04 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
Go语言基础map用法及示例详解
Nov 17 Golang
如何解决goland,idea全局搜索快捷键失效问题
Apr 03 Golang
Go语言的协程上下文的几个方法和用法
Apr 11 Golang
Golang 实现 WebSockets 之创建 WebSockets
Apr 24 Golang
Go微服务项目配置文件的定义和读取示例详解
Jun 21 Golang
Go中的条件语句Switch示例详解
Aug 23 #Golang
Go Plugins插件的实现方式
Aug 07 #Golang
使用GO语言实现Mysql数据库CURD的简单示例
Aug 07 #Golang
go使用Gin框架利用阿里云实现短信验证码功能
Aug 04 #Golang
手把手教你导入Go语言第三方库
Aug 04 #Golang
Go语言实现Base64、Base58编码与解码
Jul 26 #Golang
golang内置函数len的小技巧
Jul 25 #Golang
You might like
防止MySQL注入或HTML表单滥用的PHP程序
2009/01/21 PHP
两个开源的Php输出Excel文件类
2010/02/08 PHP
php header功能的使用
2013/10/28 PHP
Zend Framework教程之动作的基类Zend_Controller_Action详解
2016/03/07 PHP
Laravel 微信小程序后端实现用户登录的示例代码
2019/11/26 PHP
javascript 日历提醒系统( 兼容所有浏览器 )
2009/04/07 Javascript
jQuery 隔行换色 支持键盘上下键,按Enter选定值
2009/08/02 Javascript
jquery.fileEveryWhere.js 一个跨浏览器的file显示插件
2011/10/24 Javascript
使用JQUERY进行后台页面布局控制DIV实现左右式
2014/01/07 Javascript
教你用javascript实现随机标签云效果_附代码
2016/03/16 Javascript
JS+CSS3实现超炫的散列画廊特效
2016/07/16 Javascript
Bootstrap 网站实例之单页营销网站
2016/10/20 Javascript
JavaScript实现翻页功能(附效果图)
2017/02/16 Javascript
jQuery中each循环的跳出和结束实例
2017/08/16 jQuery
自适应布局meta标签中viewport、content、width、initial-scale、minimum-scale、maximum-scale总结
2017/08/18 Javascript
vue自定义过滤器创建和使用方法详解
2017/11/06 Javascript
用JS实现根据当前时间随机生成流水号或者订单号
2018/05/31 Javascript
ES6使用 Array.includes 处理多重条件用法实例分析
2020/03/02 Javascript
python 递归深度优先搜索与广度优先搜索算法模拟实现
2018/10/22 Python
Python编写合并字典并实现敏感目录的小脚本
2019/02/26 Python
解决Python中pandas读取*.csv文件出现编码问题
2019/07/12 Python
对Django外键关系的描述
2019/07/26 Python
PyCharm无法识别PyQt5的2种解决方法,ModuleNotFoundError: No module named 'pyqt5'
2020/02/17 Python
tensorflow常用函数API介绍
2020/04/19 Python
scrapy爬虫:scrapy.FormRequest中formdata参数详解
2020/04/30 Python
Python实现Appium端口检测与释放的实现
2020/12/31 Python
css3遮罩层镂空效果的多种实现方法
2020/05/11 HTML / CSS
瑞典最好的运动鞋专卖店:Sneakersnstuff
2016/08/29 全球购物
美国在线购买空气净化器、除湿器、加湿器网站:AllergyBuyersClub
2021/03/16 全球购物
企业宣传方案
2014/03/04 职场文书
浪费资源的建议书
2014/03/12 职场文书
小学红领巾广播稿(3篇)
2014/09/13 职场文书
给领导的感谢信范文
2015/01/23 职场文书
MySQL 数据丢失排查案例
2021/05/08 MySQL
「约定的梦幻岛」作画发布诺曼生日新绘
2022/03/21 日漫
Nginx动静分离配置实现与说明
2022/04/07 Servers