> Что такое состояние гонки и к чему оно приводит (Go)

Уровень: junior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Wildberries

Стек: Go, Java

> Пример ответа

Короткий ответ

Состояние гонки (race condition) - это ситуация, когда результат работы программы зависит от порядка выполнения concurrent-операций (горутин, потоков). Возникает при одновременном доступе к общим данным без синхронизации. Приводит к некорректным результатам, панике, повреждению данных, утечкам или непредсказуемому поведению. В Go часто выявляется детектором гонок -race.

Подробное объяснение

Состояние гонки возникает, когда минимум два потока (или горутины) обращаются к одной общей переменной, и хотя бы одна операция - запись, при этом нет механизма синхронизации (mutex, atomic, channel). Классический пример - инкремент счётчика: операции чтения и записи не атомарны, поэтому возможна потеря обновлений.

Последствия:

  • Некорректные данные - счётчики, балансы, кэши.
  • Паника - при обращении к map из нескольких горутин.
  • Повреждение структур данных - частично записанные значения.
  • Дедлоки - как следствие неправильной синхронизации.
  • Нестабильность - баг воспроизводится не всегда, что усложняет отладку.

В Go состояние гонки - это всегда ошибка программиста, а не рантайма. Детектор -race находит большинство таких случаев на этапе тестирования.

На практике

В backend-разработке на Go гонки чаще всего встречаются:

  • при работе с общими счётчиками метрик;
  • при конкурентном доступе к кэшам (map, slice);
  • при обновлении полей структур в обработчиках HTTP-запросов;
  • при использовании глобальных переменных.

Правила предотвращения:

  • не использовать общие данные без необходимости;
  • использовать sync.Mutex, sync.RWMutex, atomic для простых типов;
  • предпочитать каналы для передачи данных между горутинами;
  • всегда запускать тесты с -race.

Пример кода

GO
package main
import (
"fmt"
"sync"
)
var counter int
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter++ // гонка: чтение + запись не атомарны
}()
}
wg.Wait()
fmt.Println(counter) // результат будет меньше 1000
}

Исправление через mutex:

GO
var (
counter int
mu sync.Mutex
)
// внутри горутины:
mu.Lock()
counter++
mu.Unlock()

Как отвечать на собеседовании

Начни с определения: состояние гонки - это зависимость результата от порядка выполнения concurrent-операций. Приведи пример с инкрементом счётчика. Обязательно упомяни, что в Go есть встроенный детектор -race. Расскажи о способах борьбы: mutex, atomic, каналы. Если спросят про Java - упомяни synchronized, volatile, AtomicInteger, ConcurrentHashMap. Покажи понимание, что гонка - это логическая ошибка, а не случайность.

Что проверяет интервьюер

  • понимание модели памяти и атомарности операций;
  • знание базовых примитивов синхронизации;
  • умение объяснить, почему counter++ не атомарен;
  • осознание важности детектора гонок в Go;
  • способность предложить корректное решение.

Типичные ошибки

  • утверждение, что гонка возникает только при использовании нескольких ядер - это неверно, достаточно планировщика;
  • игнорирование -race при тестировании;
  • предложение sync.WaitGroup как решения - он не защищает данные;
  • использование atomic для сложных структур - только для простых типов;
  • забывание, что чтение без записи тоже может быть гонкой, если другой поток пишет.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью