> Что такое condition variable в синхронизации (Go)
Уровень: junior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: VK
Стек: Go
> Пример ответа
Короткий ответ
Condition variable - это примитив синхронизации, который позволяет горутинам ждать наступления определённого условия и получать уведомление, когда оно выполняется. В Go condition variable реализована через sync.Cond и всегда используется вместе с мьютексом. Она решает проблему busy waiting: вместо активного опроса состояния горутина блокируется и просыпается только по сигналу.
Подробное объяснение
Condition variable состоит из трёх основных операций:
Wait()- атомарно разблокирует мьютекс и блокирует горутину до получения сигнала. После пробуждения горутина снова захватывает мьютекс.Signal()- пробуждает одну горутину, ожидающую на condition variable.Broadcast()- пробуждает все ожидающие горутины.
Ключевое свойство: Wait() выполняется атомарно относительно мьютекса. Это предотвращает гонку, когда горутина проверяет условие, а между проверкой и блокировкой условие меняется.
В Go sync.Cond создаётся через sync.NewCond(&mutex). Мьютекс должен быть передан при создании и не может быть заменён.
Типичный паттерн использования:
GOmu.Lock()for !condition {cond.Wait()}// используем ресурсmu.Unlock()
Цикл for обязателен, а не if, потому что:
- Горутина может проснуться без сигнала (spurious wakeup).
- Несколько горутин могут проснуться одновременно, и условие может снова стать ложным.
На практике
В Go condition variable используется реже, чем в других языках, потому что каналы покрывают большинство сценариев. Однако sync.Cond незаменим, когда:
- Нужно уведомить несколько горутин о событии (Broadcast).
- Нужно ожидать сложное условие, которое зависит от нескольких переменных.
- Нужна более тонкая синхронизация, чем через каналы.
Примеры реального применения:
- Очередь задач с ограниченной ёмкостью: продюсеры ждут места, потребители ждут задач.
- Пул соединений: горутины ждут свободного соединения.
- Кэш с инвалидацией: все читатели ждут обновления данных.
Важно помнить: Signal() и Broadcast() должны вызываться при захваченном мьютексе. Это не строгое требование, но гарантирует корректность, если условие зависит от состояния, защищённого мьютексом.
Пример кода
GOpackage mainimport ("fmt""sync""time")type Queue struct {items []intcond *sync.Cond}func NewQueue() *Queue {return &Queue{items: make([]int, 0),cond: sync.NewCond(&sync.Mutex{}),}}func (q *Queue) Push(item int) {q.cond.L.Lock()defer q.cond.L.Unlock()q.items = append(q.items, item)// Пробуждаем одного потребителяq.cond.Signal()}func (q *Queue) Pop() int {q.cond.L.Lock()defer q.cond.L.Unlock()// Обязательно цикл, а не iffor len(q.items) == 0 {q.cond.Wait()}item := q.items[0]q.items = q.items[1:]return item}func main() {q := NewQueue()// Потребительgo func() {for i := 0; i < 3; i++ {item := q.Pop()fmt.Println("получено:", item)}}()// Продюсерfor i := 1; i <= 3; i++ {time.Sleep(100 * time.Millisecond)q.Push(i)}time.Sleep(200 * time.Millisecond)}
Как отвечать на собеседовании
Начни с определения: condition variable - примитив для ожидания условия с блокировкой вместо busy waiting. Затем объясни связку с мьютексом и почему Wait() атомарно разблокирует и блокирует. Обязательно упомяни цикл for вместо if - это ключевой момент, который проверяет понимание. Приведи пример из практики: очередь или пул ресурсов. Если спросят про альтернативы - скажи, что в Go каналы часто заменяют condition variable, но sync.Cond нужен для broadcast-уведомлений и сложных условий.
Что проверяет интервьюер
- Понимание разницы между
Signal()иBroadcast(). - Знание, почему
Wait()всегда вызывается в цикле. - Понимание атомарности
Wait()относительно мьютекса. - Умение отличать condition variable от каналов и выбирать правильный инструмент.
- Понимание опасности дедлоков при неправильном использовании.
Типичные ошибки
- Использование
ifвместоforв цикле ожидания - приводит к гонкам и панике. - Вызов
Signal()без захваченного мьютекса - может привести к потере сигнала. - Забывают, что
Wait()после пробуждения снова захватывает мьютекс - код послеWait()выполняется под блокировкой. - Путают
Signal()иBroadcast():Signal()будит одну горутину,Broadcast()- все. - Пытаются заменить condition variable каналом там, где нужен broadcast - канал с закрытием не подходит для многократных уведомлений.
> Похожие задачи по Go
Есть ли смысл объявлять мапу в конструкторе в Go вместо объявления переменной
Какие методы у мьютекса для блокировки на чтение и запись в Go
Что такое полиморфизм в Go
Как модифицировать архитектуру микросервиса, чтобы обеспечить надежность обработки заказов при падении аналитического сервиса
> Похожие задачи по backend
Есть ли смысл объявлять мапу в конструкторе в Go вместо объявления переменной
Какие методы у мьютекса для блокировки на чтение и запись в Go
Что такое полиморфизм в Go
Как модифицировать архитектуру микросервиса, чтобы обеспечить надежность обработки заказов при падении аналитического сервиса
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью