概述
Golang 中 sync 包实现了两种锁,Mutex(互斥锁)和 RWMutex(读写锁),其中 RWMutex 是基于 Mutex 实现的。
1.sync.Mutex
type Mutex struct {
// contains filtered or unexported fields
}
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
sync.Mutex 用于多个 goroutine 对共享资源的互斥访问。使用要点如下:
(1)使用 Lock() 加锁,Unlock() 解锁;
(2)对未解锁的 Mutex 使用 Lock() 会阻塞;
(3)对未上锁的 Mutex 使用 Unlock() 会导致 panic 异常。
多个 goroutine 对同一个全局变量读写需要加锁,示例如下:
package main
import (
"fmt"
"sync"
)
func main() {
var intVar int
var wg sync.WaitGroup
var mutex sync.RWMutex
go func(){
defer wg.Done()
mutex.Lock()
intVar=4
mutex.Unlock()
fmt.Printf("first goroutine, intVar=%d\n",intVar)
}()
go func(){
defer wg.Done()
mutex.Lock()
intVar=5
mutex.Unlock()
fmt.Printf("second goroutine, intVar=%d\n",intVar)
}()
wg.Add(2)
wg.Wait()
fmt.Println("end main goroutine")
}
输出结果:
second goroutine, intVar=5
first goroutine, intVar=4
end main goroutine
//或
first goroutine, intVar=4
second goroutine, intVar=5
end main goroutine
在解锁之前加锁会导致死锁。
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!
2.sync.RWMutex
type RWMutex struct {
// contains filtered or unexported fields
}
func (rw *RWMutex) Lock()
func (rw *RWMutex) RLock()
func (rw *RWMutex) RLocker() Locker
func (rw *RWMutex) RUnlock()
func (rw *RWMutex) Unlock()
sync.RWMutex 用于读锁和写锁分开的情况。使用时注意如下几点:
(1)RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁;
(2)读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁;
(3)写锁会阻止其他 goroutine(无论读和写)进来,整个锁由该 goroutine 独占;
(4)适用于读多写少的场景。
2.1 Lock()与Unlock()
(1)Lock() 加写锁,Unlock() 解写锁
(2)如果在加写锁之前已经有其他的读锁和写锁,则 Lock() 会阻塞直到该锁可用,为确保该锁可用,已经阻塞的 Lock() 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定;
(3)在 Lock() 之前使用 Unlock() 会导致 panic 异常。
2.2 RLock() 和 RUnlock()
(1)RLock() 加读锁,RUnlock() 解读锁;
(2)RLock() 加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加多个;
(3)RUnlock() 解读锁,RUnlock() 撤销单次 RLock() 调用,对于其他同时存在的读锁则没有效果;
(4)在没有读锁的情况下调用 RUnlock(),会导致 panic 错误;
(5)RUnlock() 的个数不得多于 RLock(),否则会导致 panic 错误。
Go map 的读写是非原子操作,如果有多个 goroutine 同时对 map 进行操作时,需要加读写锁。
package main
import (
"sync"
"fmt"
)
func main() {
mapNameAge :=make(map[string]int)
var rwMutex sync.RWMutex
var wg sync.WaitGroup
go func(){
defer wg.Done()
rwMutex.Lock()
mapNameAge["dablelv"]=18
rwMutex.Unlock()
fmt.Println("first goroutine to write map end")
}()
go func(){
defer wg.Done()
rwMutex.RLock()
age, _ :=mapNameAge["dablelv"]
rwMutex.RUnlock()
fmt.Printf("second goroutine to read map end, map[dablelv]=%d\n",age)
}()
wg.Add(2)
wg.Wait()
fmt.Println("main goroutine end")
}
输出结果:
second goroutine to read map end, map[dablelv]=0
first goroutine to write map end
main goroutine end
//或
first goroutine to write map end
second goroutine to read map end, map[dablelv]=18
main goroutine end
2.3 错误使用异常
(1)Unlock() 使用之前不存在 Lock()。
package main
import (
"sync"
)
func main(){
var rwMutex sync.RWMutex
rwMutex.Unlock()
}
程序输出:
panic: sync: Unlock of unlocked RWMutex
(2)RUnlock() 之前不存在 RLock()。
package main
import (
"sync"
)
func main(){
var rwMutex sync.RWMutex
rwMutex.RUnlock()
}
程序输出:
fatal error: sync: RUnlock of unlocked RWMutex
(3)RWMutex 使用不当导致的死锁。
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!
(4)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
最后
以上就是岁月静好为你收集整理的Golang sync.Mutex 与 sync.RWMutex的全部内容,希望文章能够帮你解决Golang sync.Mutex 与 sync.RWMutex所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复