Какие гарантии порядка записи в счетчик из нескольких goroutine?
Что проверяют
Знаете ли вы модель памяти Go и отличаете ли корректность итогового значения от порядка, в котором goroutine до него добрались. Следом почти всегда просят показать, как это проверить, и спрашивают про go test -race.
Как отвечать
Начните с главного: порядок между goroutine язык не гарантирует никак. Планировщик запускает их в произвольном порядке, и десять go f() в цикле не выполнятся по номерам. Синхронизация даёт отношение happens-before, а не очерёдность.
- без синхронизации:
counter++это чтение, сложение и запись. Две goroutine прочитают одно значение, и инкремент потеряется. Это гонка данных, и по модели памяти результат не определён; sync/atomic:atomic.Int64и егоAddгарантируют, что ни один инкремент не пропадёт. Модель памяти Go прямо говорит, что атомарные операции ведут себя как последовательно согласованные. Порядка между goroutine это не добавляет;sync.Mutex: каждыйUnlockпроисходит до следующего успешногоLock, поэтому изменения видны следующему владельцу. Очереди по порядку прихода нет: только если ожидающий ждёт дольше миллисекунды, мьютекс переходит в режим голодания и передаёт владение по очереди;- канал: отправка происходит до завершения соответствующего приёма, значения от одного отправителя приходят в порядке отправки. Порядок между несколькими отправителями не определён.
Если порядок действительно нужен, его задают явно: одна goroutine владеет счётчиком и читает события из канала, либо каждая запись получает номер и сортируется потом. Итог после wg.Wait() читать безопасно, потому что все Done() происходят до возврата из Wait.
Где спотыкаются
Говорят, что мьютекс выстраивает goroutine в очередь по порядку запуска. Он защищает от одновременного доступа, но кто войдёт первым, решает планировщик.
Считают, что запись в int на amd64 атомарна сама по себе, и гонки нет. Инкремент остаётся тремя операциями, а компилятор вправе держать значение в регистре и не видеть чужих записей.
Смешивают атомарную запись с обычным чтением того же поля. Детектор -race такое ловит, но только если гонка реально случилась в прогоне.