Возникает ли гонка при одновременной модификации множества горутин по индексу в Go
Что проверяют
Понимаете ли вы, что такое гонка данных по модели памяти Go, а не на уровне «горутины трогают один слайс, значит опасно». Правильный ответ неочевиден, поэтому вопрос и задают. Следом обычно спрашивают про append из нескольких горутин и про map.
Как отвечать
Короткий ответ: запись в разные элементы слайса или массива из разных горутин гонкой не является. Гонка данных это конкурентный доступ к одной и той же ячейке памяти, где хотя бы один доступ пишет. Каждый элемент слайса отдельная ячейка, и если горутина i пишет только в data[i], пересечений нет. go test -race на таком коде молчит, а мьютекс вокруг data[index] = index не нужен и только сериализует работу.
Условия, при которых это правда:
- индексы не пересекаются: две горутины пишут в один элемент, или одна пишет, а другая читает тот же элемент, это уже гонка;
- слайс не растёт:
appendиз нескольких горутин гонка, потому что меняется заголовок слайса, а при нехватке ёмкости данные переезжают в новый массив. Длину выделяют заранее черезmake([]T, n); - результат читают после синхронизации:
wg.Wait()даёт отношение happens-before, без него чтениеdataвmainгонится с записями; - это слайс, а не
map: дляmapлюбая конкурентная запись, даже по разным ключам, гонка, и рантайм падает сfatal error: concurrent map writes.
Отдельно упомяните цикл. Начиная с Go 1.22 переменная цикла своя на каждой итерации, поэтому go func() { data[i] = i }() безопасен. В старых версиях все горутины захватывали одну i, и это была настоящая гонка, которую лечили передачей параметра.
Где спотыкаются
Отвечают «да, гонка, нужен мьютекс». Код с мьютексом работает, но показывает, что кандидат не отличает общий слайс от общей ячейки памяти.
Путают корректность и производительность. Соседние элементы лежат в одной кеш-линии, и частые записи в них с разных ядер тормозят из-за false sharing. Это вопрос скорости, а не гонка.
Считают, что race detector доказывает отсутствие гонок. Он ловит только те, что реально произошли в прогоне, поэтому тесты с -race должны исполнять конкурентные сценарии.