MST

星途 面试题库

面试题:Go条件变量在复杂同步场景下的优化

假设你有一个多协程的应用,其中多个协程会读写共享资源,并且存在复杂的条件判断来决定协程的阻塞与唤醒。如何使用Go的条件变量来设计一个高效的同步机制?在实现过程中,如何避免死锁和不必要的资源竞争,描述你的设计思路并给出关键代码片段。
12.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 设计思路
    • 使用sync.Cond结构体来实现条件变量。sync.Cond需要与一个sync.Locker(如sync.Mutex)配合使用。
    • 多个协程在访问共享资源前先获取锁。
    • 当满足某些条件需要阻塞协程时,使用cond.Wait()方法,该方法会自动释放锁并阻塞当前协程,当被唤醒时会重新获取锁。
    • 当条件满足时,使用cond.Signal()cond.Broadcast()方法来唤醒等待的协程。cond.Signal()唤醒一个等待的协程,cond.Broadcast()唤醒所有等待的协程。
    • 为避免死锁,确保在调用cond.Wait()前获取了锁,并且在唤醒协程后,被唤醒的协程能够正确地重新获取锁并继续执行。同时,避免在没有获取锁的情况下调用cond.Signal()cond.Broadcast()
    • 为避免不必要的资源竞争,确保对共享资源的读写操作都在锁的保护下进行。
  2. 关键代码片段
package main

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

var (
    mu    sync.Mutex
    cond  *sync.Cond
    count int
)

func init() {
    mu = sync.Mutex{}
    cond = sync.NewCond(&mu)
    count = 0
}

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    for count < 3 {
        fmt.Printf("Worker %d waiting...\n", id)
        cond.Wait()
    }
    fmt.Printf("Worker %d resumed. Count: %d\n", id, count)
    // 对共享资源进行操作
    count++
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    time.Sleep(2 * time.Second)
    mu.Lock()
    count = 3
    fmt.Println("Broadcasting to all workers...")
    cond.Broadcast()
    mu.Unlock()

    wg.Wait()
    fmt.Println("All workers finished. Final count:", count)
}

在上述代码中:

  • musync.Mutex用于保护共享资源count
  • condsync.Cond条件变量,通过NewCond初始化并传入mu
  • worker函数是协程执行的函数,在获取锁后,通过cond.Wait()等待条件满足(count达到3)。
  • main函数中,启动多个协程后,通过修改count并调用cond.Broadcast()来唤醒所有等待的协程。