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 ...
随机推荐
- VS - Microsoft.Practices.Unity
PM> Install-Package Unity Web.config <configSections> <section name="unity" t ...
- C语言函数的定义和使用(2)
一:无参函数 类型说明符 get(){ //函数体 } 二:无参函数 类型说明符 getname(int a,int b){ //函数体 } 三:类型说明符包括: int ,char,float,do ...
- golang-切片
切片练习 package main import "fmt" /*func main() { arr := [10]int {1, 2, 3, 4, 5, 6, 7, 8, 9, ...
- java学习笔记(2)注释、public lcass、class、标识符、字面值、变量
java学习笔记(1)中说过了java的一些基础知识,发展史,特点,编译和运行过程,配置环境变量等,接下来开始介绍java的语法等只是点 关于java源程序中的注释: *什么是注释?注释的作用是什么 ...
- centos安装浏览器【备份】
chrome(谷歌) 添加源:sudo wget http://repo.fdzh.org/chrome/google-chrome-mirrors.repo -P /etc/yum.repos.d ...
- python2 && python3 的 input函数
Python2.x中的input()函数input()函数让我们明确我们输入的是数字格式还是字符格式,就是我们自己要知道我们想要的是什么,数字格式直接输入,字符格式必须加上单引号或者双引号,以确定我们 ...
- 为什么要监控sql语句,以及如何监控,都有哪几种方式可以监控。
快速阅读 为什么要监控sql语句,以及如何监控,都有哪几种方式可以监控. 我们知道sql server 中有个工具叫sql profile ,可以实时监控sql server中 执行的sql 语句,以 ...
- BS4库详解
from bs4 import BeautifulSoup html = """ <html><head><title>This is ...
- intellij idea tomcat 启动不生成war包
intellij idea tomcat 启动不生成war包 想把项目打包成war包做测试,但是按照之前的方法居然没有成功导出war包,犯了很低级的错误,特此记录. (1)首先在Project Str ...
- 后端小白的Bootstrap笔记 一
栅格系统 下面这张图是Bootstrap对栅格系统有关系数的规定 什么是栅格体统? 栅格系统是Bootstrap提供的移动优先的网格系统, 各个分界点如上: 576px 720px 992px 120 ...