MST

星途 面试题库

面试题:Go语言中sync.Cond如何与sync.Mutex配合使用

在Go语言里,sync.Cond经常与sync.Mutex一同使用。请阐述它们配合使用的基本原理,并编写一段代码示例,展示使用sync.Cond实现一个简单的生产者 - 消费者模型,要求生产者向一个共享队列中添加数据,消费者从队列中取出数据,并且要保证线程安全。
26.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

基本原理

  1. sync.Mutex:它是Go语言标准库提供的互斥锁,用于保护共享资源,确保同一时间只有一个goroutine能够访问共享资源,防止数据竞争。
  2. sync.Cond:条件变量,它需要与一个互斥锁 sync.Mutex 配合使用。sync.Cond 可以让一个或多个goroutine在满足特定条件时被唤醒。当条件不满足时,goroutine可以通过 Cond.Wait 方法释放持有的互斥锁并进入等待状态,当条件满足时,其他goroutine可以通过 Cond.SignalCond.Broadcast 方法唤醒等待的goroutine。Cond.Wait 方法在进入等待状态前会自动释放它持有的互斥锁,当被唤醒时会重新获取互斥锁。

代码示例

package main

import (
    "fmt"
    "sync"
)

type Queue struct {
    data []int
    cond *sync.Cond
    mutex sync.Mutex
}

func (q *Queue) Enqueue(item int) {
    q.mutex.Lock()
    defer q.mutex.Unlock()
    q.data = append(q.data, item)
    fmt.Printf("Produced: %d\n", item)
    q.cond.Broadcast()
}

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

func main() {
    queue := &Queue{
        cond: sync.NewCond(&queue.mutex),
    }

    var wg sync.WaitGroup
    wg.Add(2)

    // 生产者
    go func() {
        defer wg.Done()
        for i := 0; i < 5; i++ {
            queue.Enqueue(i)
        }
    }()

    // 消费者
    go func() {
        defer wg.Done()
        for i := 0; i < 5; i++ {
            queue.Dequeue()
        }
    }()

    wg.Wait()
}

在这段代码中:

  1. Queue 结构体包含一个整数数组 data 作为共享队列,一个 sync.Cond 类型的条件变量 cond 和一个 sync.Mutex 类型的互斥锁 mutex
  2. Enqueue 方法用于向队列中添加数据,它首先获取互斥锁,添加数据后通过 cond.Broadcast 唤醒所有等待的goroutine。
  3. Dequeue 方法用于从队列中取出数据,它先获取互斥锁,当队列为空时通过 cond.Wait 等待,直到被唤醒且队列中有数据时取出数据。
  4. main 函数中启动了一个生产者goroutine和一个消费者goroutine,分别向队列中添加和取出数据。