GraphLMS

Go
0/32 решеноНачать
ТопикиТопик 02

Синхронизация и sync

0/4 решено0%

О чём этот блок

Каналы хороши для оркестрации — «передай работу, сообщи о завершении». Но когда речь идёт о защите общего состояния (счётчик, кэш, флаг инициализации), на первый план выходят примитивы пакета 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.

Связанные главы учебника

// Идиома блока: разделяем читателей и писателей под 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
}

Задачи топика 4

05Thread-safe Counter (Mutex vs atomic)functionaleasy06Потокобезопасный кэш (95% reads)functionaleasy07Idempotent Initializer (sync.Once)functionaleasy08WaitGroup своими рукамиfunctionalmedium