Go 语言中的锁机制提供了多种方式来控制并发访问,以确保数据的一致性和安全性。以下是 Go 中常用的几种锁:
1. sync.Mutex
(互斥锁)
- 用途:控制对共享资源的独占访问,只允许一个 goroutine 持有锁,从而防止数据竞争。
- 使用方法:调用
Lock()
加锁,Unlock()
解锁。 - 适用场景:适合需要完全互斥的场景,比如对共享变量的写操作。
示例:
var mu sync.Mutexfunc increment() {mu.Lock()counter++mu.Unlock()
}
2. sync.RWMutex
(读写锁)
- 用途:允许多个 goroutine 同时读取,但写操作是独占的,能提高读多写少场景的性能。
- 使用方法:
RLock()
获取读锁,RUnlock()
释放读锁;Lock()
获取写锁,Unlock()
释放写锁。
- 适用场景:适合读多写少的场景,比如缓存、配置文件等。
示例:
var mu sync.RWMutexfunc 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.Oncefunc 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() // 通知一个等待中的 goroutinecond.L.Unlock()
}
5. sync.Map
(并发安全的 Map)
- 用途:实现并发安全的键值对存储,适合高并发环境下的读写操作。
- 使用方法:提供
Store
、Load
、Delete
、Range
等方法来操作 map。 - 适用场景:适用于大量的读写操作,比如缓存等场景。
示例:
var m sync.Mapfunc 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.WaitGroupfunc 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 int32func increment() {atomic.AddInt32(&counter, 1)
}
总结
- Mutex 和 RWMutex 是基础的锁,分别用于一般的互斥和读写场景。
- Once、Cond、WaitGroup、和 atomic 则提供了更高级的并发控制,适用于不同的场景。
- sync.Map 是线程安全的 map,适合高并发场景,避免手动加锁。