TGViewer
Библиотека Go (Golang) разработчика Библиотека Go (Golang) разработчика @golang_lib · 2.7K subscribers
Post #710 327
📢 sync.Cond: Как разбудить 10 000 горутин одним вызовом (и не сломать планировщик)

Представьте задачу: у вас запущено 10 000 горутин-воркеров, которые должны начать обработку данных строго одновременно, но только после того, как в память загрузится тяжелый кэш или конфигурация.

Как передать им сигнал "на старт"?

Вариант 1: Каналы.
Если мы отправим сигнал в канал ch <- struct{}{}, проснется только одна горутина. Чтобы разбудить 10 000, придется писать цикл. Долго и неэффективно.
Можно сделать close(ch) - закрытие канала мгновенно разбудит все горутины, читающие из него. Но канал можно закрыть только один раз. Что если нам нужно подавать такой массовый сигнал каждые 5 минут при обновлении кэша?

Здесь на сцену выходит sync.Cond (Condition Variable) - один из самых недооцененных примитивов синхронизации.

Это рупор, через который одна горутина может крикнуть толпе спящих горутин: "Событие произошло, просыпайтесь!"

Как это работает

У sync.Cond есть три главных метода:

* Wait() - усыпить текущую горутину до получения сигнала.
* Signal() - разбудить одну случайную спящую горутину.
* Broadcast() - разбудить все спящие горутины разом.

Чтобы sync.Cond работал, ему обязательно нужен мьютекс (sync.Mutex или sync.RWMutex), который защищает условие.

✅ Как это выглядит в коде:


type AppState struct {
mu sync.Mutex
cond *sync.Cond
configLoad bool
}

func NewAppState() *AppState {
a := &AppState{}
// Cond всегда связывается с мьютексом
a.cond = sync.NewCond(&a.mu)
return a
}

func (a *AppState) Worker(id int) {
a.mu.Lock() // Обязательно лочим мьютекс перед Wait!

// 🤡 Джун напишет тут 'if'. Сеньор пишет 'for'. Почему — ниже.
for !a.configLoad {
// Wait атомарно снимает лок с a.mu и усыпляет горутину.
// Когда она проснется, Wait атомарно вернет лок на место.
a.cond.Wait()
}

a.mu.Unlock() // Разлочили и пошли работать
fmt.Printf("Воркер %d начал работу!\n", id)
}

func (a *AppState) UpdateConfig() {
a.mu.Lock()
a.configLoad = true
a.mu.Unlock()

// БУМ! Будим все тысячи спящих горутин одним вызовом
a.cond.Broadcast()
}



🔥 Senior Tip: Почему Wait() всегда пишут в цикле for?

Главная ловушка sync.Cond заключается в том, что вызов Wait() нельзя использовать внутри if !condition { cond.Wait() }.
Спецификация POSIX-систем и рантайм Go допускают Spurious Wakeups (Ложные пробуждения). Горутина может проснуться просто так, даже если никто не вызывал Broadcast.
Кроме того, между моментом, когда горутина проснулась, и моментом, когда она захватила мьютекс для проверки, другая горутина могла успеть поменять состояние configLoad обратно на false.
Цикл for гарантирует, что проснувшаяся горутина обязательно еще раз проверит условие, прежде чем пойти дальше.

Когда sync.Cond НЕ нужен?
Если событие происходит только один раз за время жизни приложения (например, Graceful Shutdown) - не тащите sync.Cond. Обычного закрытия канала close(done) или использования context.Context хватит за глаза. sync.Cond блистает именно в сценариях многоразовых массовых оповещений.

#golang #concurrency #sync #underhood #architecture

📲 Мы в MAX

👉 @golang_lib
  • 👍 2
  • 🔥 1
More from @golang_lib
  1. Sep 22, 2026🔴AI кодинг интервью с разработчиком из международного FinTech в четверг в 19:00 ДА! Вайбк…
  2. Sep 17, 2026🔎 pprof: Как найти функцию, которая жрет 80% CPU Сервис на проде внезапно упирается в пол…
  3. Sep 7, 2026🗺️ sync.Map: Почему эта «серебряная пуля» иногда пробивает дно производительности Как тол…
  4. Sep 2, 2026☢️ Пакет unsafe: Взламываем систему типов Go Пакет unsafe - это легальный способ сказать к…
  5. Sep 1, 2026🔴 Тестовое собеседование с Go Senior с опытом работы в Яндексе, EPAM и Uzum в этот четвер…
  6. Sep 1, 2026Memory Alignment: Как сэкономить гигабайты RAM, просто поменяв строчки местами Вы написали…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →