MST

星途 面试题库

面试题:Go sync.Cond的原理与应用

Go语言的`sync.Cond`用于条件变量,它基于一个锁(通常是`sync.Mutex`或`sync.RWMutex`)来协调多个goroutine的同步。请描述`sync.Cond`的原理,包括`Wait`、`Signal`和`Broadcast`方法的作用,以及在实际应用中如何使用`sync.Cond`来解决生产者 - 消费者问题,给出详细的代码实现。
41.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

sync.Cond原理

sync.Cond基于一个底层锁(sync.Mutexsync.RWMutex)来实现条件变量。它的主要作用是让一组goroutine在满足特定条件时被唤醒。

Wait方法

  • Wait方法会释放与之关联的锁,并阻塞当前goroutine,将其放入等待队列。
  • Wait被唤醒时(通过SignalBroadcast),它会重新获取锁并继续执行。

Signal方法

  • Signal方法唤醒等待队列中的一个随机的goroutine。
  • 如果没有等待的goroutine,Signal不做任何操作。

Broadcast方法

  • Broadcast方法唤醒等待队列中的所有goroutine。
  • 如果没有等待的goroutine,Broadcast不做任何操作。

生产者 - 消费者问题代码实现

package main

import (
    "fmt"
    "sync"
)

type Queue struct {
    items []int
    max   int
    mu    sync.Mutex
    cond  *sync.Cond
}

func NewQueue(max int) *Queue {
    q := &Queue{
        max: max,
    }
    q.cond = sync.NewCond(&q.mu)
    return q
}

func (q *Queue) Enqueue(item int) {
    q.mu.Lock()
    for len(q.items) == q.max {
        q.cond.Wait()
    }
    q.items = append(q.items, item)
    fmt.Printf("Produced: %d\n", item)
    q.cond.Signal()
    q.mu.Unlock()
}

func (q *Queue) Dequeue() int {
    q.mu.Lock()
    for len(q.items) == 0 {
        q.cond.Wait()
    }
    item := q.items[0]
    q.items = q.items[1:]
    fmt.Printf("Consumed: %d\n", item)
    q.cond.Signal()
    q.mu.Unlock()
    return item
}

func main() {
    queue := NewQueue(2)

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        for i := 1; i <= 3; i++ {
            queue.Enqueue(i)
        }
    }()

    go func() {
        defer wg.Done()
        for i := 1; i <= 3; i++ {
            queue.Dequeue()
        }
    }()

    wg.Wait()
}

在上述代码中:

  1. Queue结构体包含一个items切片用于存储数据,max表示队列的最大容量,mu是一个互斥锁,cond是条件变量。
  2. NewQueue函数初始化Queue并创建条件变量。
  3. Enqueue方法在队列满时调用Wait阻塞,直到有空间;添加元素后调用Signal唤醒等待的消费者。
  4. Dequeue方法在队列为空时调用Wait阻塞,直到有数据;取出元素后调用Signal唤醒等待的生产者。
  5. main函数启动一个生产者和一个消费者,并等待它们完成。