通八洲科技

如何在Golang中使用sync实现并发控制_互斥锁和条件变量

日期:2025-12-26 00:00 / 作者:P粉602998670
sync.Mutex 用于保护临界区确保互斥访问,sync.Cond 需配合 Mutex 实现条件等待与通知;二者协同解决“谁可以进”和“等什么再进”的问题,使用时须遵循锁→检查→等待/修改→通知→解锁流程。

在 Go 中,sync 包提供了基础的并发原语,其中 互斥锁(sync.Mutex条件变量(sync.Cond 是控制共享资源访问、协调 goroutine 执行的关键工具。它们不单独使用,而是配合协作:Mutex 保证临界区安全,Cond 在满足特定条件时唤醒等待的 goroutine。

用 sync.Mutex 保护共享数据

Mutex 是最常用的同步机制,用于确保同一时刻只有一个 goroutine 能进入临界区。它不关心“为什么等”,只负责“谁可以进”。

示例:安全地累加计数器

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value++
}

func (c *Counter) Load() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

用 sync.Cond 实现条件等待与通知

sync.Cond 本身不提供互斥能力,必须和一个已有的 sync.Locker(通常是 *sync.Mutex)绑定。它解决的是“等某个条件成立再继续”的问题,比如“队列非空才取元素”或“缓冲区有空位才写入”。

示例:实现一个线程安全的阻塞队列

type BlockingQueue struct {
    mu       sync.Mutex
    cond     *sync.Cond
    items    []int
    capacity int
}

func NewBlockingQueue(cap int) *BlockingQueue {
    q := &BlockingQueue{
        capacity: cap,
        items:    make([]int, 0),
    }
    q.cond = sync.NewCond(&q.mu)
    return q
}

func (q *BlockingQueue) Push(x int) {
    q.mu.Lock()
    defer q.mu.Unlock()
    // 等待有空位
    for len(q.items) >= q.capacity {
        q.cond.Wait()
    }
    q.items = append(q.items, x)
    q.cond.Broadcast() // 通知可能等待读取的 goroutine
}

func (q *BlockingQueue) Pop() int {
    q.mu.Lock()
    defer q.mu.Unlock()
    // 等待非空
    for len(q.items) == 0 {
        q.cond.Wait()
    }
    x := q.items[0]
    q.items = q.items[1:]
    q.cond.Broadcast() // 通知可能等待写入的 goroutine
    return x
}

常见误区与注意事项

使用 Cond 时容易出错,关键点在于逻辑顺序和状态一致性:

对比 channel:何时选 Cond?

Go 推崇 “通过通信共享内存”,所以大多数场景优先用 channel。但 Cond 仍有其适用位置:

简单说:能用 channel 就别硬上 Cond;需要用 Cond 时,一定配好 Mutex,且严格遵循“锁 → 检查 → 等待 / 修改 → 通知 → 解锁”流程。