golang中sync.RWMutex和sync.Mutex区别
golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能.
-
type Mutex
- func (m *Mutex) Lock()
- func (m *Mutex) Unlock()
- type RWMutex
- func (rw *RWMutex) Lock()
- func (rw *RWMutex) RLock()
- func (rw *RWMutex) RLocker() Locker
- func (rw *RWMutex) RUnlock()
-
func (rw *RWMutex) Unlock()
其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁叶叫做全局锁.
func (m *Mutex) Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误.
已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁.
正常运行例子:
- package main
- import (
- "fmt"
- "sync"
- )
- func main() {
- var l *sync.Mutex
- l = new(sync.Mutex)
- l.Lock()
- defer l.Unlock()
- fmt.Println("1")
- }
- 结果输出:1
当Unlock()在Lock()之前使用时,便会报错
- package main
- import (
- "fmt"
- "sync"
- )
- func main() {
- var l *sync.Mutex
- l = new(sync.Mutex)
- l.Unlock()
- fmt.Println("1")
- l.Lock()
- }
- 运行结果: panic: sync: unlock of unlocked mutex
当在解锁之前再次进行加锁,便会死锁状态
- package main
- import (
- "fmt"
- "sync"
- )
- func main() {
- var l *sync.Mutex
- l = new(sync.Mutex)
- l.Lock()
- fmt.Println("1")
- l.Lock()
- }
- 运行结果: 1
- fatal error: all goroutines are asleep - deadlock!
RWMutex是一个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读次数远远多于写次数的场景.
func (rw *RWMutex) Lock() 写锁,如果在添加写锁之前已经有其他的读锁和写锁,则lock就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的 Lock 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
func (rw *RWMutex) Unlock() 写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误.
- package main
- import (
- "fmt"
- "sync"
- )
- func main() {
- var l *sync.RWMutex
- l = new(sync.RWMutex)
- l.Unlock()
- fmt.Println("1")
- l.Lock()
- }
- 运行结果:panic: sync: unlock of unlocked mutex
func (rw *RWMutex) RLock() 读锁,当有写锁时,无法加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景
func (rw *RWMutex)RUnlock() 读锁解锁,RUnlock 撤销单次 RLock 调用,它对于其它同时存在的读取器则没有效果。若 rw 并没有为读取而锁定,调用 RUnlock 就会引发一个运行时错误(注:这种说法在go1.3版本中是不对的,例如下面这个例子)。
- package main
- import (
- "fmt"
- "sync"
- )
- func main() {
- var l *sync.RWMutex
- l = new(sync.RWMutex)
- l.RUnlock() //1个RUnLock
- fmt.Println("1")
- l.RLock()
- }
- 运行结果:1
- 但是程序中先尝试 解锁读锁,然后才加读锁,但是没有报错,并且能够正常输出.
分析:go1.3版本中出现这种情况的原因分析,通过阅读源码可以很清晰的得到结果
- func (rw *RWMutex) RUnlock() {
- if raceenabled {
- _ = rw.w.state
- raceReleaseMerge(unsafe.Pointer(&rw.writerSem))
- raceDisable()
- }<span style="color:#FF0000;">
- if atomic.AddInt32(&rw.readerCount, -1) < 0 { //readercounter初始值为0,调用RUnLock之后变为-1,继续往下执行
- // A writer is pending.
- if atomic.AddInt32(&rw.readerWait, -1) == 0 { //此时readerwaiter变为1,1-1之后变为0,可以继续以后的操作.</span>
- // The last reader unblocks the writer.
- runtime_Semrelease(&rw.writerSem)
- }
- }
- if raceenabled {
- raceEnable()
- }
- }
当RUnlock多于RLock多个时,便会报错,进入死锁.实例如下:
- package main
- import (
- "fmt"
- "sync"
- )
- type s struct {
- readerCount int32
- }
- func main() {
- l := new(sync.RWMutex)
- l.RUnlock()
- l.RUnlock() //此处出现死锁
- fmt.Println("1")
- l.RLock()
- }
- 运行结果:
- 1
- fatal error: all goroutines are asleep - deadlock!
总结:
所以在go1.3版本中,运行过程中允许RUnLock早于RLock一个,也只能早于1个(注:虽然代码允许,但是强烈不推荐使用),并且在早于之后必须利用RLock进行加锁才可以继续使用.
golang中sync.RWMutex和sync.Mutex区别的更多相关文章
- golang中,new和make的区别
在golang中,make和new都是分配内存的,但是它们之间还是有些区别的,只有理解了它们之间的不同,才能在合适的场合使用. 简单来说,new只是分配内存,不初始化内存: 而make即分配又初始化内 ...
- Golang 读写锁RWMutex 互斥锁Mutex 源码详解
前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...
- 【GoLang】golang中 channel 实现同步 与mutex/atomic 实现同步的讨论
参考资料: https://groups.google.com/forum/#!topic/golang-china/q4pFH-AGnfs
- golang中锁
一.什么场景下需要用到锁当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,有可能是多个线程同时访问公共资源,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? ...
- golang 中 sync.Mutex 的实现
mutex 的实现思想 mutex 主要有两个 method: Lock() 和 Unlock() Lock() 可以通过一个 CAS 操作来实现 func (m *Mutex) Lock() { f ...
- golang中并发sync和channel
golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...
- Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)
什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...
- golang 中 sync包的 WaitGroup
golang 中的 sync 包有一个很有用的功能,就是 WaitGroup 先说说 WaitGroup 的用途:它能够一直等到所有的 goroutine 执行完成,并且阻塞主线程的执行,直到所有的 ...
- golang sync.RWMutex
sync.RWMutex package main import ( "fmt" "runtime" "sync" ) func click ...
随机推荐
- 使用apache 的 ab命令压力测试nginx服务器
nginx压力测试方法: #ab命令 #安装ab #Centos系统 yum install apr-util #Ubuntu系统 sudo apt-get install apache2-utils ...
- 洛谷 P1190 接水问题 题解
P1190 接水问题 题目描述 学校里有一个水房,水房里一共装有 \(m\) 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为1. 现在有 \(n\) 名同学准备接水,他们的初始接水顺序已经确 ...
- redis系列(四):切换RDB备份到AOF备份
1.准备环境 redis.conf服务端配置如下: daemonize yes port logfile /data//redis.log dir /data/ dbfilename dbmp.rdb ...
- (2)Angular的开发
什么是 ReactNative应用, 小程序与RN的关系 native端优化 Js端优化 ReactNative是? 用JavaScript来写App,性能好 异步编程 请点赞!因为你的鼓励是我写作的 ...
- A. Vasya and Book ( Codeforces Educational Codeforces Round 55 )
题意:当前在看书的第 x 页,每次可以向前或者向后翻 d 页,这个书一共 n 页,问能否用最小操作翻到第 y 页. 题解:三种情况:1.直接翻能到的一定最短. 2.先翻到第一页,然后往后翻,翻到第 y ...
- JSP简单标签的开发
1. 新建RepeatSampleTag类,代码如下: package bid.zhazhapan.fims.tag; import java.io.IOException; import java. ...
- ubuntu之路——day7.2 regularization
所有的正则化方法来自于吴恩达老师的免费公开课:https://mooc.study.163.com/learn/2001281003?tid=2001391036#/learn/content?typ ...
- C# 复制数组容易踩到的坑--引用类型与值类型
原文链接:https://my.oschina.net/u/3744313/blog/1794235 笔者近期做的项目里大量使用了数组,而在使用过程中,笔者曾经遇到了一个比较低级的问题:如何将一个数组 ...
- CRM 线索 客户 统称为 资源 客户服务管理篇 销售易
线索 客户 统称为 资源 - 国内版 Binghttps://cn.bing.com/search?FORM=U227DF&PC=U227&q=%E7%BA%BF%E7%B4%A2+% ...
- lintcode 394. Coins in a Line 、leetcode 292. Nim Game 、lintcode 395. Coins in a Line II
变型:如果是最后拿走所有石子那个人输,则f[0] = true 394. Coins in a Line dp[n]表示n个石子,先手的人,是必胜还是必输.拿1个石子,2个石子之后都是必胜,则当前必败 ...