分享
  1. 首页
  2. 文章

golang 中 sync.Mutex 和 sync.RWMutex

WangZZ · · 3016 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。


介绍

golang 中的 sync 包实现了两种锁:

  • Mutex:互斥锁
  • RWMutex:读写锁,RWMutex 基于 Mutex 实现

Mutex(互斥锁)

  • Mutex 为互斥锁,Lock() 加锁,Unlock() 解锁
  • 在一个 goroutine 获得 Mutex 后,其他 goroutine 只能等到这个 goroutine 释放该 Mutex
  • 使用 Lock() 加锁后,不能再继续对其加锁,直到利用 Unlock() 解锁后才能再加锁
  • 在 Lock() 之前使用 Unlock() 会导致 panic 异常
  • 已经锁定的 Mutex 并不与特定的 goroutine 相关联,这样可以利用一个 goroutine 对其加锁,再利用其他 goroutine 对其解锁
  • 在同一个 goroutine 中的 Mutex 解锁之前再次进行加锁,会导致死锁
  • 适用于读写不确定,并且只有一个读或者写的场景

示例

加锁和解锁示例

package main
import (
 "time"
 "fmt"
 "sync"
)
func main() {
 var mutex sync.Mutex
 fmt.Println("Lock the lock")
 mutex.Lock()
 fmt.Println("The lock is locked")
 channels := make([]chan int, 4)
 for i := 0; i < 4; i++ {
 channels[i] = make(chan int)
 go func(i int, c chan int) {
 fmt.Println("Not lock: ", i)
 mutex.Lock()
 fmt.Println("Locked: ", i)
 time.Sleep(time.Second)
 fmt.Println("Unlock the lock: ", i)
 mutex.Unlock()
 c <- i
 }(i, channels[i])
 }
 time.Sleep(time.Second)
 fmt.Println("Unlock the lock")
 mutex.Unlock()
 time.Sleep(time.Second)
 for _, c := range channels {
 <-c
 }
}

程序输出:

Lock the lock
The lock is locked
Not lock: 1
Not lock: 2
Not lock: 0
Not lock: 3
Unlock the lock
Locked: 1
Unlock the lock: 1
Locked: 2
Unlock the lock: 2
Locked: 3
Unlock the lock: 3
Locked: 0
Unlock the lock: 0

在解锁之前加锁会导致死锁

package main
import (
 "fmt"
 "sync"
)
func main(){
 var mutex sync.Mutex
 mutex.Lock()
 fmt.Println("Locked")
 mutex.Lock()
}

程序输出:

Locked
fatal error: all goroutines are asleep - deadlock!

RWMutex(读写锁)

  • RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁
  • 读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁
  • 写锁会阻止其他 goroutine(无论读和写)进来,整个锁由该 goroutine 独占
  • 适用于读多写少的场景

Lock() 和 Unlock()

  • Lock() 加写锁,Unlock() 解写锁
  • 如果在加写锁之前已经有其他的读锁和写锁,则 Lock() 会阻塞直到该锁可用,为确保该锁可用,已经阻塞的 Lock() 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
  • 在 Lock() 之前使用 Unlock() 会导致 panic 异常

RLock() 和 RUnlock()

  • RLock() 加读锁,RUnlock() 解读锁
  • RLock() 加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加载多个
  • RUnlock() 解读锁,RUnlock() 撤销单词 RLock() 调用,对于其他同时存在的读锁则没有效果
  • 在没有读锁的情况下调用 RUnlock() 会导致 panic 错误
  • RUnlock() 的个数不得多余 RLock(),否则会导致 panic 错误

示例

Lock() 和 Unlock()

package main
import (
 "sync"
 "fmt"
 "time"
)
func main() {
 var mutex *sync.RWMutex
 mutex = new(sync.RWMutex)
 fmt.Println("Lock the lock")
 mutex.Lock()
 fmt.Println("The lock is locked")
 channels := make([]chan int, 4)
 for i := 0; i < 4; i++ {
 channels[i] = make(chan int)
 go func(i int, c chan int) {
 fmt.Println("Not lock: ", i)
 mutex.Lock()
 fmt.Println("Locked: ", i)
 fmt.Println("Unlock the lock: ", i)
 mutex.Unlock()
 c <- i
 }(i, channels[i])
 }
 time.Sleep(time.Second)
 fmt.Println("Unlock the lock")
 mutex.Unlock()
 time.Sleep(time.Second)
 for _, c := range channels {
 <-c
 }
}

程序输出:

Lock the lock
The lock is locked
Not lock: 0
Not lock: 1
Not lock: 2
Not lock: 3
Unlock the lock
Locked: 0
Unlock the lock: 0
Locked: 2
Unlock the lock: 2
Locked: 3
Unlock the lock: 3
Locked: 1
Unlock the lock: 1

Lock() 和 RLock()

package main
import (
 "sync"
 "fmt"
 "time"
)
func main() {
 var mutex *sync.RWMutex
 mutex = new(sync.RWMutex)
 fmt.Println("Lock the lock")
 mutex.Lock()
 fmt.Println("The lock is locked")
 channels := make([]chan int, 4)
 for i := 0; i < 4; i++ {
 channels[i] = make(chan int)
 go func(i int, c chan int) {
 fmt.Println("Not read lock: ", i)
 mutex.RLock()
 fmt.Println("Read Locked: ", i)
 fmt.Println("Unlock the read lock: ", i)
 time.Sleep(time.Second)
 mutex.RUnlock()
 c <- i
 }(i, channels[i])
 }
 time.Sleep(time.Second)
 fmt.Println("Unlock the lock")
 mutex.Unlock()
 time.Sleep(time.Second)
 for _, c := range channels {
 <-c
 }
}

程序输出:

Lock the lock
The lock is locked
Not read lock: 2
Not read lock: 3
Not read lock: 1
Not read lock: 0
Unlock the lock
Read Locked: 2
Read Locked: 1
Unlock the read lock: 2
Unlock the read lock: 1
Read Locked: 0
Read Locked: 3
Unlock the read lock: 0
Unlock the read lock: 3

Unlock() 使用之前不存在 Lock()

package main
import (
 "sync"
)
func main(){
 var rwmutex *sync.RWMutex
 rwmutex = new(sync.RWMutex)
 rwmutex.Unlock()
}

程序输出:

panic: sync: Unlock of unlocked RWMutex

RWMutex 使用不当导致的死锁

示例1:

package main
import (
 "sync"
)
func main(){
 var rwmutex *sync.RWMutex
 rwmutex = new(sync.RWMutex)
 rwmutex.Lock()
 rwmutex.Lock()
}

程序输出:

fatal error: all goroutines are asleep - deadlock!

示例2:

package main
import (
 "sync"
)
func main(){
 var rwmutex *sync.RWMutex
 rwmutex = new(sync.RWMutex)
 rwmutex.Lock()
 rwmutex.RLock()
}

程序输出:

fatal error: all goroutines are asleep - deadlock!

RUnlock() 之前不存在 RLock()

package main
import (
 "sync"
)
func main(){
 var rwmutex *sync.RWMutex
 rwmutex = new(sync.RWMutex)
 rwmutex.RUnlock()
}

程序输出:

panic: sync: RUnlock of unlocked RWMutex

RUnlock() 个数多于 RLock()

package main
import (
 "sync"
)
func main(){
 var rwmutex *sync.RWMutex
 rwmutex = new(sync.RWMutex)
 rwmutex.RLock()
 rwmutex.RLock()
 rwmutex.RUnlock()
 rwmutex.RUnlock()
 rwmutex.RUnlock()
}

程序输出:

panic: sync: RUnlock of unlocked RWMutex

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:WangZZ

查看原文:golang 中 sync.Mutex 和 sync.RWMutex

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3016 次点击 ∙ 1 赞
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏