MST

星途 面试题库

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

假设存在一个高并发场景,多个协程会频繁地等待和唤醒条件变量。请分析在这种情况下,如何避免条件变量的虚假唤醒,以及如何优化性能,例如减少锁竞争。同时,请说明`Broadcast`和`Signal`方法在这种场景下的使用选择及原因。
29.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

避免条件变量的虚假唤醒

在高并发场景下,虚假唤醒是指线程在没有满足预期条件的情况下被唤醒。为避免虚假唤醒,应在条件变量等待的循环中检查条件,而不是只检查一次。例如在Go语言中:

cond.L.Lock()
for!condition {
    cond.Wait()
}
// 执行满足条件后的操作
cond.L.Unlock()

在其他语言如C++中也类似:

std::unique_lock<std::mutex> lock(mutex);
while (!condition) {
    cond.wait(lock);
}
// 执行满足条件后的操作

这样即使发生虚假唤醒,线程会再次检查条件,若条件不满足则继续等待。

优化性能,减少锁竞争

  1. 减少锁的粒度:只在必要时锁定临界区。例如将大的临界区拆分成多个小的临界区,尽量缩短持有锁的时间。
  2. 使用读写锁:如果读操作远多于写操作,可以使用读写锁。读操作时多个协程可以同时获取读锁,写操作时获取写锁,这样可以减少锁竞争。
  3. 无锁数据结构:对于一些场景,可以使用无锁数据结构,如无锁队列、无锁哈希表等,从根本上避免锁竞争。但无锁数据结构实现复杂,且需要特定的硬件支持。

BroadcastSignal方法的使用选择及原因

  1. Broadcast
    • 使用场景:当所有等待条件变量的协程都需要被唤醒去检查条件时,使用Broadcast。例如在一个资源池被重置,所有等待获取资源的协程都需要重新检查资源是否可用的场景。
    • 原因:它会唤醒所有等待在条件变量上的协程,这些协程被唤醒后会竞争锁,然后检查条件。如果只有部分协程需要被唤醒,可能会造成不必要的唤醒和锁竞争。
  2. Signal
    • 使用场景:当只有一个等待条件变量的协程需要被唤醒时,使用Signal。例如在一个生产者 - 消费者模型中,生产者生产了一个数据,只需要唤醒一个消费者协程来处理这个数据。
    • 原因:它只会唤醒一个等待在条件变量上的协程,相比Broadcast减少了不必要的唤醒和锁竞争,能提高性能。