Как избежать конкурентного доступа к флагу для управления состоянием в Go?
Что проверяют
Понимаете ли вы, что гонка на флаге это не теоретическая проблема, а ошибка по модели памяти Go: без синхронизации нет гарантии, что другая горутина вообще увидит новое значение. Следом обычно просят показать сценарий «проверил и выставил» и спрашивают, как вы нашли бы такую гонку в существующем коде.
Как отвечать
Начните с того, почему обычный bool не годится: компилятор вправе держать значение в регистре или вынести чтение из цикла, и горутина будет крутиться вечно. То, что запись одного байта атомарна на железе, модель памяти Go не обещает.
Дальше варианты по задаче:
atomic.Bool: типизированный атомик изsync/atomic(с Go 1.19).StoreиLoadдля простого флага,CompareAndSwap(false, true)для «сделать ровно один раз». В старом коде встречаетсяint32сatomic.LoadInt32, это тот же приём без типизации;sync.Mutexилиsync.RWMutex: когда флаг меняется вместе с другими полями и они должны оставаться согласованными. Атомик защищает одно значение, а не инвариант между несколькими;context.Contextили закрытие канала: если флаг означает «остановись», идиоматичнееctx.Done()илиclose(done). Закрытый канал видят все горутины сразу, и его можно ждать вselect;sync.Once: если флаг нужен только для однократной инициализации;- владение состоянием одной горутиной: остальные шлют ей команды через канал, общей памяти нет вовсе.
Закончите проверкой: go test -race и go run -race ловят гонки только в тех путях, которые реально выполнились, поэтому детектор включают в CI на тестах с параллельной нагрузкой.
Где спотыкаются
Защищают запись, а чтение оставляют голым. Гонка никуда не девается: читать без синхронизации так же нельзя, как писать.
Пишут if !flag.Load() { flag.Store(true); start() }. Между проверкой и записью успевает проскочить вторая горутина, и start() вызывается дважды. Здесь нужен CompareAndSwap.
Копируют структуру с мьютексом по значению, например через метод с получателем не по указателю. Каждая копия блокирует свой мьютекс, и защиты нет. go vet с проверкой copylocks такое находит.