go-互斥锁与读写锁
- Go 里的互斥锁和读写锁,本质上都是为了解决:多个 goroutine 并发访问共享数据时,避免数据竞争(data race)。
- 互斥锁Mutex:同一时间只允许一个 goroutine 进入。
- 读写锁RWMutex:允许多个 goroutine 同时读,但写的时候必须独占。
- Go 主要通过
sync包使用锁。
为什么需要锁
- 如果多个 goroutine 同时修改一个变量,会产生数据竞争问题,导致数据不准确
- 保护多个 goroutine 共享的数据,防止它们同时修改或读写数据,产生数据竞争
互斥锁
- Go 里的互斥锁 Mutex解决的是**:**多个 goroutine 同时访问、修改同一份共享数据时,避免发生数据竞争
- 通过标准库的sync.Mutex来实现互斥锁
- 用来保护多个 goroutine 共同访问的共享资源
- 通过互斥锁来保护资源
- 使同一时间只有一个goroutine能访问
互斥理解
- 互:多个 goroutine 之间
- 斥:互相排斥,不能同时进入
读写锁
- 读写锁
RWMutex:读可以多人同时读;写还是独占 RLock():读锁- 只想读数据,不修改数据
defer Unlock
- defer:把一个函数调用延迟到当前函数结束前执行
- defer Unlock:保证当前函数结束时自动释放锁。
mu.Lock()
defer mu.Unlock()
count++
使用场景
多个goroutine协程改同一个内存数据问题,避免出现数据竞争,加上锁
缓存
- 缓存的特点:读的次数比较多,写比较少
- 有人要改缓存,需要加锁, 其他人需要等待,等他改完再继续
配置读取
- 支付配置
- 系统配置
- 在修改配置时,就先锁住,等修改完成后再继续读
计数器
- 统计请求次数
- 比如多个请求同时进来,这些请求都同时修改数据,不加锁的话,导致最终的统计的次数不正确
- 多个请求进来,那就排队,一个改完,下一个再改
var mu sync.Mutex
func add() {
mu.Lock()
defer mu.Unlock()
count++
}
map 的并发安全
- go的map不是并发安全的
- 如果2个不同的goroutine同时读和写,多个线程同时操作一个map。go会直接pannic报错,导致竟态条件,产生数据竞争
- 属于并发读写,不安全