Go语言锁笔记
Go 语言中的锁机制提供了多种方式来控制并发访问,以确保数据的一致性和安全性。以下是 Go 中常用的几种锁:
1. sync.Mutex
(互斥锁)
- 用途:控制对共享资源的独占访问,只允许一个 goroutine 持有锁,从而防止数据竞争。
- 使用方法:调用
Lock()
加锁,Unlock()
解锁。 - 适用场景:适合需要完全互斥的场景,比如对共享变量的写操作。
示例:
var mu sync.Mutex
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
2. sync.RWMutex
(读写锁)
- 用途:允许多个 goroutine 同时读取,但写操作是独占的,能提高读多写少场景的性能。
- 使用方法:
RLock()
获取读锁,RUnlock()
释放读锁;Lock()
获取写锁,Unlock()
释放写锁。
- 适用场景:适合读多写少的场景,比如缓存、配置文件等。
示例:
var mu sync.RWMutex
func read() {
mu.RLock()
defer mu.RUnlock()
fmt.Println(counter)
}
func write() {
mu.Lock()
defer mu.Unlock()
counter++
}
3. sync.Once
(单次锁)
- 用途:确保某些初始化操作只执行一次。
- 使用方法:调用
Do(func)
,传入的函数只会执行一次,无论多少 goroutine 调用Do
。 - 适用场景:适用于单次初始化的场景,比如单例模式或仅初始化一次的资源。
示例:
var once sync.Once
func initialize() {
once.Do(func() {
fmt.Println("Initializing...")
})
}
4. sync.Cond
(条件锁)
- 用途:用于协调多个 goroutine 等待和通知操作,通常配合
Mutex
使用。 - 使用方法:
Wait()
等待条件满足并自动释放锁;Signal()
唤醒一个等待的 goroutine;Broadcast()
唤醒所有等待的 goroutine。
- 适用场景:适合用于需要等待条件的场景,比如生产者-消费者模型。
示例:
var mu sync.Mutex
var cond = sync.NewCond(&mu)
func waitForCondition() {
cond.L.Lock()
cond.Wait() // 等待通知
fmt.Println("Condition met")
cond.L.Unlock()
}
func signalCondition() {
cond.L.Lock()
cond.Signal() // 通知一个等待中的 goroutine
cond.L.Unlock()
}
5. sync.Map
(并发安全的 Map)
- 用途:实现并发安全的键值对存储,适合高并发环境下的读写操作。
- 使用方法:提供
Store
、Load
、Delete
、Range
等方法来操作 map。 - 适用场景:适用于大量的读写操作,比如缓存等场景。
示例:
var m sync.Map
func main() {
m.Store("key", "value")
if v, ok := m.Load("key"); ok {
fmt.Println(v)
}
m.Delete("key")
}
6. sync.WaitGroup
(等待组)
- 用途:用于等待一组并发操作完成。
- 使用方法:
Add(n)
添加要等待的 goroutine 数量;Done()
表示某个 goroutine 完成;Wait()
阻塞直到所有 goroutine 完成。
- 适用场景:适用于需要等待多个 goroutine 完成的场景,比如并发任务的汇总。
示例:
var wg sync.WaitGroup
func worker() {
defer wg.Done()
fmt.Println("Working...")
}
func main() {
wg.Add(2)
go worker()
go worker()
wg.Wait() // 等待所有 worker 完成
}
7. atomic
包(原子操作)
- 用途:提供底层的原子操作,避免使用锁,但依赖于硬件的原子指令。
- 使用方法:
atomic.AddInt32
、atomic.LoadInt32
、atomic.StoreInt32
等。 - 适用场景:适合简单计数、状态切换等轻量级并发场景,避免锁开销。
示例:
import "sync/atomic"
var counter int32
func increment() {
atomic.AddInt32(&counter, 1)
}
总结
- Mutex 和 RWMutex 是基础的锁,分别用于一般的互斥和读写场景。
- Once、Cond、WaitGroup、和 atomic 则提供了更高级的并发控制,适用于不同的场景。
- sync.Map 是线程安全的 map,适合高并发场景,避免手动加锁。