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=====

posted on   运维开发玄德公  阅读(17)  评论(0编辑  收藏  举报  

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示