全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-13 8 分钟 ✍️ juanwangdev

Go并发同步原语详解

sync包提供底层同步原语,用于保护共享资源和协调goroutine。

Mutex互斥锁

基本用法

Go
var mu sync.Mutex
var counter int

func increment() {
    mu.Lock()
    counter++
    mu.Unlock()
}

防止死锁

Go
// 错误:忘记Unlock
func bad() {
    mu.Lock()
    if condition {
        return  // 死锁!
    }
    mu.Unlock()
}

// 正确:使用defer
func good() {
    mu.Lock()
    defer mu.Unlock()  // 确保释放

    if condition {
        return  // 安全
    }
}

永远用defer释放锁,防止死锁。

RWMutex读写锁

读多写少场景

Go
var rwmu sync.RWMutex
var data map[string]string

func read(key string) string {
    rwmu.RLock()      // 读锁,可并行
    v := data[key]
    rwmu.RUnlock()
    return v
}

func write(key, val string) {
    rwmu.Lock()       // 写锁,互斥
    data[key] = val
    rwmu.Unlock()
}
锁类型读操作写操作适用场景
Mutex互斥互斥读写均衡
RWMutex并行互斥读多写少

WaitGroup等待组

等待多个goroutine完成

Go
var wg sync.WaitGroup

func main() {
    for i := 0; i < 5; i++ {
        wg.Add(1)        // 增加计数
        go func(n int) {
            defer wg.Done()  // 减少计数
            process(n)
        }(i)
    }
    wg.Wait()            // 等待全部完成
}

注意事项

Go
// 错误:Add在goroutine内
go func() {
    wg.Add(1)  // 可能Wait已执行!
    defer wg.Done()
}()

// 正确:Add在启动前
wg.Add(1)
go func() {
    defer wg.Done()
}()

Add必须在goroutine启动前调用。

Once单次执行

Go
var once sync.Once
var resource *Resource

func initResource() {
    once.Do(func() {
        resource = &Resource{}  // 只执行一次
    })
}

// 多次调用只执行一次
initResource()
initResource()  // 不执行

Cond条件变量

等待条件满足

Go
var mu sync.Mutex
var cond = sync.NewCond(&mu)
var ready bool

func waitReady() {
    mu.Lock()
    defer mu.Unlock()

    for !ready {
        cond.Wait()  // 等待信号,释放锁
    }
    // 条件满足
}

func signalReady() {
    mu.Lock()
    ready = true
    cond.Broadcast()  // 唤醒所有等待者
    // 或 cond.Signal() 唤醒一个
    mu.Unlock()
}

Wait内部释放锁,返回前重新获取锁。

Pool对象池

Go
var pool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 1024)
    },
}

func processData() {
    buf := pool.Get().([]byte)
    defer pool.Put(buf)

    // 使用buf
    // ...
}

// Pool对象可能被GC回收
// 不适合长期存储

Map并发安全map

Go
var m sync.Map

// 存储
m.Store("key", "value")

// 读取
v, ok := m.Load("key")

// 删除
m.Delete("key")

// 遍历
m.Range(func(key, value interface{}) bool {
    fmt.Println(key, value)
    return true  // 继续遍历
})

sync.Map适合读多写少场景,纯写性能不如Mutex+map。

同步原语选择

原语用途特点
Mutex保护共享资源简单互斥
RWMutex读多写少读并行
WaitGroup等待完成协调goroutine
Once单次初始化懒加载
Cond等待条件状态同步
Pool对象复用减少GC
sync.Map并发map读多写少优

常见陷阱

1. 锁未释放

Go
mu.Lock()
// panic或return导致未Unlock

2. 复制锁

Go
// 错误:复制Mutex
var mu sync.Mutex
mu2 := mu  // 复制!锁状态丢失

// 正确:使用指针
var mu *sync.Mutex = &sync.Mutex{}

3. Wait的for循环

Go
// 错误:Wait不在循环中
if !ready {
    cond.Wait()  // 可能虚假唤醒
}

// 正确:Wait在循环中
for !ready {
    cond.Wait()  // 处理虚假唤醒
}

要点总结

  • Mutex用于互斥保护,必须defer Unlock
  • RWMutex适合读多写少,RLock可并行
  • WaitGroup.Add在goroutine启动前调用
  • Once.Do保证只执行一次
  • Cond.Wait必须在循环中检查条件
  • Pool复用临时对象,不适合长期存储
  • sync.Map读多写少场景性能优
  • 锁不能复制,使用指针传递

📝 发现内容有误?点击此处直接编辑

← 上一篇 Go常见并发模式详解
下一篇 → Go竞态检测与并发调试详解
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库