当前位置: 面试刷题>> Go 语言中,Cond 的 Wait 方法怎么使用?


在Go语言中,sync.Cond 是一个用于协调多个goroutine之间等待与通知的同步原语,它通常与 sync.Mutexsync.RWMutex 一起使用,以确保在条件等待和条件信号发送时的互斥访问。CondWait 方法是 sync.Cond 类型中非常核心的一个方法,它用于阻塞当前goroutine,直到另一个goroutine通过调用 SignalBroadcast 方法来唤醒它。

使用 sync.Cond 的基本步骤

  1. 创建并初始化:首先,你需要一个 sync.Mutex(或 sync.RWMutex)和一个 sync.Cond 实例。sync.Cond 必须在初始化时与一个已存在的互斥锁关联。

  2. 锁定互斥锁:在调用 Wait 方法之前,必须首先锁定与 Cond 关联的互斥锁。

  3. 检查条件:在 Wait 方法调用之前,通常会有一个条件检查,以确定是否真的需要等待。

  4. 调用 Wait 方法:调用 Wait 方法会原子地释放互斥锁并使当前goroutine阻塞,直到另一个goroutine调用 SignalBroadcast 方法。

  5. 重新检查条件:从 Wait 返回后,互斥锁会被重新获取,此时应重新检查条件是否满足。

  6. 解锁互斥锁:在条件满足并处理完相关逻辑后,应释放互斥锁。

示例代码

下面是一个使用 sync.Cond 的示例,模拟了一个简单的生产者-消费者问题,其中 Cond 用于控制消费者等待生产者生产数据。

package main

import (
    "fmt"
    "sync"
    "time"
)

// 假设这是一个共享的数据结构
type Buffer struct {
    items []int
    cond  *sync.Cond
    mutex sync.Mutex
}

func NewBuffer(size int) *Buffer {
    return &Buffer{
        items: make([]int, 0, size),
        cond:  sync.NewCond(&sync.Mutex{}), // 注意这里传入的是互斥锁的指针
    }
}

// 生产者
func (b *Buffer) Produce(item int) {
    b.mutex.Lock()
    defer b.mutex.Unlock()

    b.items = append(b.items, item)
    fmt.Printf("Produced: %d\n", item)
    b.cond.Signal() // 通知一个等待的消费者
}

// 消费者
func (b *Buffer) Consume() {
    b.mutex.Lock()
    defer b.mutex.Unlock()

    for len(b.items) == 0 {
        fmt.Println("Waiting for item...")
        b.cond.Wait() // 等待生产者生产数据
    }

    item := b.items[0]
    b.items = b.items[1:]
    fmt.Printf("Consumed: %d\n", item)
}

func main() {
    buffer := NewBuffer(5)

    // 启动生产者
    go func() {
        for i := 0; i < 10; i++ {
            buffer.Produce(i)
            time.Sleep(time.Second) // 模拟生产耗时
        }
    }()

    // 启动消费者
    for i := 0; i < 5; i++ {
        go buffer.Consume()
        time.Sleep(2 * time.Second) // 交错启动消费者
    }

    // 等待一段时间,确保所有goroutine都执行完毕
    time.Sleep(10 * time.Second)
}

注意事项

  • 在使用 sync.Cond 时,务必确保在调用 Wait 方法之前已经锁定了互斥锁,并且在 Wait 返回后重新检查条件,因为可能存在虚假唤醒(spurious wakeup)的情况。
  • Signal 方法唤醒一个等待的goroutine(如果有的话),而 Broadcast 方法唤醒所有等待的goroutine。选择哪个方法取决于你的具体需求。
  • 示例中使用了 time.Sleep 来模拟生产者和消费者的不同速度,以及确保主goroutine在退出前等待其他goroutine完成。在实际应用中,可能需要更复杂的同步机制来确保程序的正确性和效率。

通过上述示例和解释,你应该对如何在Go语言中使用 sync.CondWait 方法有了更深入的理解。希望这能帮助你在面试中脱颖而出,并在实际开发中更有效地利用Go的并发特性。

推荐面试题