ТопикиТопик 02
Синхронизация и sync
О чём этот блок
Каналы хороши для оркестрации — «передай работу, сообщи о завершении».
Но когда речь идёт о защите общего состояния (счётчик, кэш, флаг
инициализации), на первый план выходят примитивы пакета sync и
sync/atomic. Этот блок строит навык, без которого не обойтись ни в одном
сервисе: безопасно читать и менять данные из множества горутин, не превращая
горячий путь в узкое место.
Ключевой принцип Go-сообщества: «Share memory by communicating» — но это не
догма. Счётчик, протектящий каждую операцию каналом, будет медленнее мьютекса,
а read-heavy кэш под sync.Mutex упрётся в контеншен там, где RWMutex или
atomic.Value дали бы кратный выигрыш. Здесь вы учитесь выбирать инструмент
под профиль нагрузки, а не по привычке.
Что вы научитесь делать
- Защищать счётчик мьютексом и атомиком и понимать, когда какой быстрее.
- Оптимизировать read-heavy структуры через
sync.RWMutex(или copy-on-write). - Делать инициализацию идемпотентной и безопасной: ровно один запуск, общий
результат для всех ожидающих (
sync.Once). - Видеть, что happens-before гарантирует видимость записей между горутинами, а его отсутствие — это data race.
- Понимать внутреннее устройство
WaitGroup, реализовав его аналог на каналах и условиях.
Карта задач
- 05 · Thread-safe Counter (Mutex vs atomic) — две реализации одного счётчика; цена блокировки против lock-free инкремента; контеншен под тысячами горутин.
- 06 · Потокобезопасный кэш (95% reads) — оптимизация под read-heavy
профиль:
RWMutexпротивMutex, разделение читателей и писателей. - 07 · Idempotent Initializer (sync.Once) — «выполнить ровно один раз»: все вызывающие ждут единственного запуска и получают один и тот же результат (включая ошибку).
- 08 · WaitGroup своими руками — устройство барьера ожидания изнутри:
синхронизация счётчика и пробуждение
Wait()безsync.WaitGroupи без прямогоatomic.
Связанные главы учебника
- sync: Mutex, RWMutex, Once, Cond, WaitGroup, Pool —
когда мьютекс, когда канал;
RWMutexдля read-heavy;CondиOnce. - sync/atomic — атомики против мьютекса, contention, lock-free счётчики.
- Модель памяти и happens-before — почему без синхронизации запись из одной горутины может быть не видна другой.
// Идиома блока: разделяем читателей и писателей под read-heavy нагрузкой.
func (c *Cache) Get(k string) (any, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
v, ok := c.m[k]
return v, ok
}