golang并发编程-05-同步-01-锁的使用(sync包Mutex、RWMutex结构体的常用方法)
1.golang并发编程-01多进程编程-01概述2.golang并发编程-01多进程编程-02管道3.golang并发编程-01多进程编程-03信号4.golang并发编程-01多进程编程-04socket5.golang并发编程-02多线程编程-01线程概述6.golang并发编程-02多线程编程-02线程的同步7.golang并发编程-03-协程(Goroutine)概述8.golang并发编程-04-通道-01-基本使用/缓冲通道/非缓冲通道9.golang并发编程-04-通道-02-定时器、断续器
10.golang并发编程-05-同步-01-锁的使用(sync包Mutex、RWMutex结构体的常用方法)
11.golang并发编程-05-同步-02-条件变量(Cond结构体的常用方法)12.golang并发编程-05-同步-03-原子操作(atomic包)13.golang并发编程-05-同步-04-sync包的once、WaitGroup结构体的常用方法14.golang并发编程-05-同步-05-临时对象池(sync.Pool)@
目录
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=====
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了