golang并发编程-05-同步-01-锁的使用(sync包Mutex、RWMutex结构体的常用方法)
@
目录
1. 互斥锁
1.1. 结构体 Mutex
1.1.1 结构体定义
type Mutex struct {
state int32
sema uint32
}
1.1.2 结构体下方法
- Lock()
- lockSlow()
- Unlock()
- unlockSlow(new int32)
1.2 Lock()/Unlock()方法
1.2.1 语法
- 获取锁
func (m *Mutex) Lock()
- 释放锁
func (m *Mutex) Unlock()
- 示例
import (
"sync"
)
func main() {
var mutex sync.Mutex
func() {
mutex.Lock()
defer mutex.Unlock()
}()
}
2.2.2 示例(赤壁之战——锁的争夺)
说明:魏蜀吴三个协程争夺锁,一个退出后,另一个才能获得锁。
func main() {
var mutex sync.Mutex
fmt.Println("======长江天险构造完成=======")
for _,i := range []string{"刘备","曹操","孙权"} {
go func(i string) {
fmt.Printf("【%s】 出兵 \n", i)
mutex.Lock()
defer mutex.Unlock()
fmt.Printf("【%s】 +++++占据长江+++++ \n", i)
time.Sleep(3*time.Second)
fmt.Printf("【%s】 退出长江 ====》 \n", i)
}(i)
}
time.Sleep(10*time.Second)
fmt.Println("=====END 折戟沉沙=====")
}
结果输出
======长江天险构造完成=======
【孙权】 出兵
【孙权】 +++++占据长江+++++
【刘备】 出兵
【曹操】 出兵
【孙权】 退出长江 ====》
【刘备】 +++++占据长江+++++
【刘备】 退出长江 ====》
【曹操】 +++++占据长江+++++
【曹操】 退出长江 ====》
=====END 折戟沉沙=====
2. 读写锁
特点
- 许任意个读操作同时进行
- 同一时刻,只允许有一个写操作
2.1 结构体 RWMutex
结构体
type RWMutex struct {
w Mutex
writerSem uint32
readerSem uint32
readerCount int32
readerWait int32
}
该结构体方法
- RLock()
- RUnlock()
- rUnlockSlow(r int32)
- Lock()
- Unlock()
- RLocker() sync.Locker
2.2 示例(献帝驾到——读锁间不互斥)
如下,三个协程使用读锁同时进行。
func main() {
var rwLock sync.RWMutex
var wg sync.WaitGroup
wg.Add(3)
fmt.Println("====== 献帝驾到 =======")
for _,i := range []string{"刘备","曹操","孙权"} {
go func(i string) {
rwLock.RLock()
defer rwLock.RUnlock()
fmt.Printf("【%s】 觐见 +++++ \n", i)
time.Sleep(3*time.Second)
fmt.Printf("【%s】 退出 ====》\n", i)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("=====END=====")
}
结果
====== 献帝驾到 =======
【孙权】 觐见 +++++
【刘备】 觐见 +++++
【曹操】 觐见 +++++
【曹操】 退出 ====》
【孙权】 退出 ====》
【刘备】 退出 ====》
===== END =====
2.3 示例(衣带诏——读写锁是互斥的)
读锁和写锁是互斥的
- 读锁锁定后,写锁的线程等待。
- 写锁锁定后,其他写锁和读锁线程都等待。
示例
献帝密诏董承,传衣带诏。其他人不能在此时觐见。
(其他人除了曹操我都是瞎写的)
func main() {
var rwLock sync.RWMutex
var wg sync.WaitGroup
wg.Add(5)
fmt.Println("====== 献帝驾到 =======")
go func() {
rwLock.Lock()
defer rwLock.Unlock()
fmt.Println("【董承】 被密诏 !!!!")
time.Sleep(3*time.Second)
fmt.Println("【董承】 退出 ====》")
wg.Done()
}()
for _,i := range []string{"曹操","孙权","袁绍","马腾"} {
go func(i string) {
rwLock.RLock()
defer rwLock.RUnlock()
fmt.Printf("【%s】 觐见 +++++ \n", i)
time.Sleep(3*time.Second)
fmt.Printf("【%s】 退出 ====》\n", i)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("=====END=====")
}
结果(因为每个人是一个协程,所以每次打印结果顺序不同)
====== 献帝驾到 =======
【马腾】 觐见 +++++
【马腾】 退出 ====》
【董承】 被密诏 !!!!
【董承】 退出 ====》
【袁绍】 觐见 +++++
【曹操】 觐见 +++++
【孙权】 觐见 +++++
【曹操】 退出 ====》
【孙权】 退出 ====》
【袁绍】 退出 ====》
=====END=====
如上:
- 马腾先到,觐见是读锁。
- 董承第二个到,密诏是写锁,董承等待读锁解锁后进入。
- 之后,曹操、孙权、袁绍依次到达,觐见是读锁,不会互斥,一起进入。
2.4 示例3(三顾茅庐——读锁是可重入锁)
一个协程可多次获取读锁(在该读锁解锁前)
- 示例
刘备可以多次面见诸葛亮,获得诸葛亮的读锁,而不必先解锁。
func main() {
var rwLock sync.RWMutex
var wg sync.WaitGroup
wg.Add(3)
fmt.Println("====== 三顾茅庐 =======")
go func() {
for i:=1;i<=3;i++{
rwLock.RLock()
fmt.Printf("【刘备】第 %d 次拜访【诸葛亮】 \n", i)
time.Sleep(time.Second)
}
for j:=1;j<=3;j++{
rwLock.RUnlock()
wg.Done()
}
}()
wg.Wait()
fmt.Println("=====END=====")
}
结果:
====== 三顾茅庐 =======
【刘备】第 1 次拜访【诸葛亮】
【刘备】第 2 次拜访【诸葛亮】
【刘备】第 3 次拜访【诸葛亮】
=====END=====