Что такое гонка данных и в каких системах она возникает
Что проверяют
Умеете ли вы показать гонку не на учебном счётчике, а в устройстве обычного сервиса. В Go конкурентность не опция: каждый входящий запрос уже обрабатывается в своей горутине, поэтому вопрос сводится к тому, какое состояние у вас общее.
Как отвечать
Определение короткое: два обращения к одной ячейке памяти из разных горутин, не упорядоченные отношением «происходит раньше», хотя бы одно на запись.
Дальше стоит перечислить, где это возникает на практике:
- поля структуры обработчика и пакетные переменные: конфигурация, которую перечитывают на лету, счётчики,
mapпод кэш; - ленивая инициализация клиента или соединения через проверку на nil вместо
sync.Once; - замыкания, запущенные через
goи захватившие переменные внешней функции; - значения, отданные наружу по каналу или указателем, если отправитель продолжает их менять;
- сторонние библиотеки, про которые нигде не сказано, что они потокобезопасны.
Безопасны по умолчанию только каналы и примитивы из sync и sync/atomic. Срез, строка, интерфейс, поле структуры и особенно map такой гарантии не дают, а конкурентная запись в map завершает процесс. Ловить нужно детектором гонок на прогоне тестов.
Где спотыкаются
Называют инструментом обнаружения статический анализ. go vet находит родственные ошибки, например копирование структуры вместе с мьютексом, но сами гонки видит только динамический детектор и только на реально выполненном коде.
Считают, что каналы делают безопасным всё. Несколько отправителей в один канал это штатный режим, а вот закрывать канал со стороны отправителей, когда их несколько, нельзя: будет паника. И передача указателя по каналу не отбирает доступ у того, кто его послал.
Защищают структуру мьютексом и раздают её по значению. Копия уносит с собой копию блокировки, каждая сторона запирает свой замок, а данные под указателем внутри остаются общими, так что защита исчезает незаметно.