简述
Golang中的锁机制主要包含互斥锁和读写锁
互斥锁
Gosync.Mutex
一个简单的示例:
func mutex() {
var mu sync.Mutex
mu.Lock()
fmt.Println("locked")
mu.Unlock()
}
deferUnlock
func mutex() {
var mu sync.Mutex
mu.Lock()
defer mu.Unlock()
fmt.Println("locked")
}
sync.Mutex
var mu sync.Mutex
成对出现锁加锁
func mutex() {
var mu sync.Mutex
mu.Lock()
fmt.Println("parent locked")
mu.Lock()
fmt.Println("sub locked")
mu.Unlock()
mu.Unlock()
}
fatal error: all goroutines are asleep - deadlock!
fatal error: sync: unlock of unlocked mutex
func mutex() {
var mu sync.Mutex
mu.Lock()
fmt.Println("locked")
mu.Unlock()
mu.Unlock()
}
goroutine
func mutex() {
var mu sync.Mutex
fmt.Println("parent lock start")
mu.Lock()
fmt.Println("parent locked")
for i := 0; i <= 2; i++ {
go func(i int) {
fmt.Printf("sub(%d) lock start\n", i)
mu.Lock()
fmt.Printf("sub(%d) locked\n", i)
time.Sleep(time.Microsecond * 30)
mu.Unlock()
fmt.Printf("sub(%d) unlock\n", i)
}(i)
}
time.Sleep(time.Second * 2)
mu.Unlock()
fmt.Println("parent unlock")
time.Sleep(time.Second * 2)
}
先看上面的函数执行结果
parent lock start
parent locked
sub(0) lock start
sub(2) lock start
sub(1) lock start
parent unlock // 必须等到父级先解锁,后面则会阻塞
sub(0) locked // 解锁后子goroutine才能执行锁定
sub(0) unlock
sub(2) locked
sub(2) unlock
sub(1) locked
sub(1) unlock
time.Sleep()goroutinegoroutineLockmainUnlockgoroutine
总结:
goroutinegoroutinegoroutine
读写锁
读写锁和互斥锁不同之处在于,可以分别针对读操作和写操作进行分别锁定,这样对于性能有一定的提升。
读写锁,对于多个写操作,以及写操作和读操作之前都是互斥的这一点基本等同于互斥锁。
但是对于同时多个读操作之前却非互斥关系,这也是相读写锁性能高于互斥锁的主要原因。
读写锁也是开箱即用型的
var rwm = sync.RWMutex
读写锁分为写锁和读锁:
rwm.Lock()
rwm.Unlock()
rwm.RLock()
rwm.RUnlock()
panic
func rwMutex() {
var rwm sync.RWMutex
rwm.Lock()
fmt.Println("locked")
rwm.RUnlock()
}
fatal error: sync: RUnlock of unlocked RWMutex
对于读写锁,同一资源可以同时有多个读锁定,如:
func rwMutex() {
var rwm sync.RWMutex
rwm.RLock()
rwm.RLock()
rwm.RLock()
fmt.Println("locked")
rwm.RUnlock()
rwm.RUnlock()
rwm.RUnlock()
}
deadlockpanic
func rwMutex() {
var rwm sync.RWMutex
rwm.Lock()
rwm.Lock()
rwm.Lock()
fmt.Println("locked")
rwm.Unlock()
rwm.Unlock()
rwm.Unlock()
}
goroutinegoroutine
goroutine
下面看一个完整示例:
func rwMutex() {
var rwm sync.RWMutex
for i := 0; i <= 2; i++ {
go func(i int) {
fmt.Printf("go(%d) start lock\n", i)
rwm.RLock()
fmt.Printf("go(%d) locked\n", i)
time.Sleep(time.Second * 2)
rwm.RUnlock()
fmt.Printf("go(%d) unlock\n", i)
}(i)
}
// 先sleep一小会,保证for的goroutine都会执行
time.Sleep(time.Microsecond * 100)
fmt.Println("main start lock")
// 当子进程都执行时,且子进程所有的资源都已经Unlock了
// 父进程才会执行
rwm.Lock()
fmt.Println("main locked")
time.Sleep(time.Second)
rwm.Unlock()
}
go(0) start lock
go(0) locked
go(1) start lock
go(1) locked
go(2) start lock
go(2) locked
main start lock
go(2) unloc
go(0) unlock
go(1) unlock
main locked
goroutinemainsleep 100msgoroutinegoroutinesleep 2smaingoroutine
再看一个读锁定示例:
func rwMutex5() {
var rwm sync.RWMutex
for i := 0; i <= 2; i++ {
go func(i int) {
fmt.Printf("go(%d) start lock\n", i)
rwm.RLock()
fmt.Printf("go(%d) locked\n", i)
time.Sleep(time.Second * 2)
rwm.RUnlock()
fmt.Printf("go(%d) unlock\n", i)
}(i)
}
fmt.Println("main start lock")
rwm.RLock()
fmt.Println("main locked")
time.Sleep(time.Second * 10)
}
main start lock
main locked
go(1) start lock
go(1) locked
go(2) start lock
go(2) locked
go(0) start lock
go(0) locked
go(0) unlock
go(1) unlock
go(2) unlock
goroutine
总结:
goroutine