Go同步与锁优化(内存性能视角)
锁机制影响CPU调度和内存分配,优化锁可提升整体性能。
锁的内存开销
Mutex内存结构
Go
type Mutex struct {
state int32
sema uint32
}
// 每个Mutex约8字节
// 但竞争时产生额外开销:
// - 等待队列内存
// - 上下文切换栈内存
// - CPU缓存失效
锁竞争的内存影响
Go
// 高竞争场景
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
// 多goroutine竞争时:
// 1. goroutine等待队列增长
// 2. CPU缓存频繁失效
// 3. 内存屏障触发
锁粒度优化
分拆锁
Go
// 不推荐:单一大锁
var bigLock sync.Mutex
var data [100]int
func update(i int, v int) {
bigLock.Lock()
data[i] = v
bigLock.Unlock()
}
// 推荐:分拆锁减少竞争
var locks [100]sync.Mutex
func update(i int, v int) {
locks[i].Lock()
data[i] = v
locks[i].Unlock()
}
分段锁
Go
const SHARD_COUNT = 16
type ConcurrentMap struct {
shards [SHARD_COUNT]struct {
sync.Mutex
m map[string]int
}
}
func (cm *ConcurrentMap) getShard(key string) *shard {
return &cm.shards[hash(key)%SHARD_COUNT]
}
func (cm *ConcurrentMap) Set(key string, val int) {
shard := cm.getShard(key)
shard.Lock()
shard.m[key] = val
shard.Unlock()
}
RWMutex优化读多场景
Go
// 读多写少场景用RWMutex
var rwlock sync.RWMutex
var data map[string]string
func Read(key string) string {
rwlock.RLock() // 读锁,可并行
v := data[key]
rwlock.RUnlock()
return v
}
func Write(key, val string) {
rwlock.Lock() // 写锁,互斥
data[key] = val
rwlock.Unlock()
}
| Mutex | RWMutex | 适用场景 |
|---|---|---|
| 读写互斥 | 读可并行 | 读多写少用RWMutex |
| 简单场景 | 复杂读写 | 纯写用Mutex |
| 内存小 | 内存稍大 | RWMutex开销稍高 |
无锁优化
atomic原子操作
Go
var counter int64
// 无锁原子增加
func increment() {
atomic.AddInt64(&counter, 1)
}
// 无锁读取
func getCount() int64 {
return atomic.LoadInt64(&counter)
}
// Compare And Swap
func tryUpdate(old, new int64) bool {
return atomic.CompareAndSwapInt64(&counter, old, new)
}
atomic无锁、零等待队列开销、CPU缓存友好。
channel替代锁
Go
// 用channel实现共享状态
type Counter struct {
ch chan func(int) int
val int
}
func NewCounter() *Counter {
c := &Counter{ch: make(chan func(int) int)}
go func() {
for f := range c.ch {
c.val = f(c.val)
}
}()
return c
}
func (c *Counter) Increment() {
c.ch <- func(v int) int { return v + 1 }
}
sync包组件选择
Go
// 临时对象池:sync.Pool
var pool = sync.Pool{New: func() interface{} {
return make([]byte, 1024)
}}
// 单次执行:sync.Once
var once sync.Once
func initResource() {
once.Do(func() {
// 只执行一次
})
}
// 等待组:sync.WaitGroup
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
// ...
}()
}
wg.Wait()
// 条件变量:sync.Cond
cond := sync.NewCond(&mu)
cond.Wait()
cond.Signal()
cond.Broadcast()
锁优化检查清单
| 问题 | 解决方案 |
|---|---|
| 高竞争 | 分拆锁/分段锁 |
| 读多写少 | RWMutex |
| 简单计数 | atomic |
| 状态管理 | channel |
| 临时对象 | sync.Pool |
| 长锁持时间 | 缩小锁范围 |
性能测试
Go
func BenchmarkMutex(b *testing.B) {
var mu sync.Mutex
for i := 0; i < b.N; i++ {
mu.Lock()
counter++
mu.Unlock()
}
}
func BenchmarkAtomic(b *testing.B) {
for i := 0; i < b.N; i++ {
atomic.AddInt64(&counter, 1)
}
}
func BenchmarkChannel(b *testing.B) {
ch := make(chan struct{}, 1)
for i := 0; i < b.N; i++ {
ch <- struct{}{}
counter++
<-ch
}
}
要点总结
- 锁竞争导致等待队列内存开销
- 分拆锁/分段锁减少竞争
- 读多写少用RWMutex
- 简单操作用atomic替代锁
- channel可替代锁管理状态
- sync.Pool复用临时对象
- 缩小锁持有范围减少竞争时间
📝 发现内容有误?点击此处直接编辑