首页 > 其他分享 >【转】go Mutex (互斥锁)和RWMutex(读写锁)

【转】go Mutex (互斥锁)和RWMutex(读写锁)

时间:2023-02-13 15:11:17浏览次数:41  
标签:rw RWMutex fmt sync 互斥 Mutex func go

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()
}
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,可以继续以后的操作.
// 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()
}
运行结果:

fatal error: all goroutines are asleep - deadlock!
总结:
所以在 go1.3 版本中,运行过程中允许 RUnLock 早于 RLock 一个,也只能早于1个(注:虽然代码允许,但是强烈不推荐使用),并且在早于之后必须利用 RLock 进行加锁才可以继续使用.

标签:rw,RWMutex,fmt,sync,互斥,Mutex,func,go
From: https://www.cnblogs.com/acodedreamer/p/17116468.html

相关文章

  • Leangoo领歌敏捷开发工具可将AC一键转换为任务。(检查项转换卡片)
    Leangoo领歌看板工具中企业版支持检查项转换卡片功能。可以将卡片中的每一个检查项转换为独立的卡片,方便对其分别进行任务分配和状态管理。点击检查项清单右侧的转换图标:......
  • leangoo领歌共享协作思维导图导入导出XMind文件。
    协作型共享多人协作思维导图Leangoo,可以导出导入XMind文件了,我们来看看如何操作:进入leangoo官网:www.leangoo.com,登陆账号。点击右上角“+”新建脑图,然后导入XMind文件......
  • go排序方法总结
    1、方法总结sort.Intssort.Floatssort.Stringssort.SliceStablesort.Interface{}2、实例//sort.Ints()s:=[]int{4,2,3,1}sort.Ints(s)fmt.Println(s)//......
  • 【转载】go.sum中特殊hash如何计算
    Golang为了依赖的安全考虑,在go.mod的基础上引入了go.sum,go.sum文件的作用主要是记录项目依赖的hash值,防止被人修改。在分析具体项目的go.sum文件后可以发现go.sum中不仅......
  • Calling C++ Code From Go With SWIG
    http://zacg.github.io/blog/2013/06/06/calling-c-plus-plus-code-from-go-with-swig/ RecentlywhileworkingonaGobasedprojectIneededtousesomefunctio......
  • 【问题讨论】关于golang调用so的问题的讨论
    runtime:dlopen/dlsymwithoutCGo#18296 Open  iamacarpetopenedthisissueDec13,2016·12comments  Open  ......
  • go连接kafka
    Part1前言本文主要介绍如何通过go语言连接kafka。这里采用的是sarama库。​​https://github.com/Shopify/sarama​​Part2库的安装goget-ugithub.com/Shopify/saramago......
  • golang 切片 slice
    1.基本介绍切片是数组的一个引用,因此切片是引用类型。切片的使用与数组类似,遍历,访问切片元素等都一样。切片是长度是可以变化的,因此切片可以看做是一个动态数组。slice内......
  • MixGo CE及外接模块管脚简单介绍
    之前用习惯了Arduino系列的板卡,也习惯了上面的管脚使用方法,换成MixGo系列的板卡,都是TypeC接口,这个管脚应该怎么看呢?MixGo系列的外接模块管脚又是怎么看,程序里面应该怎么......
  • 10 Django中间件
    Django中间件中间件介绍django中间件类似于是django的保安,请求来的时候需要先经过中间件才能到达django后端,响应走的时候也需要经过中间件才能到达web服务网关接口dja......