Как сделать слайс в Go безопасным для конкурентного доступа
Что проверяют
Понимаете ли вы, что именно ломается при конкурентной работе со слайсом, и умеете ли выбрать механизм под профиль нагрузки. Уточняют обычно так: а если читателей в сотни раз больше, чем писателей, и что возвращает ваш Get.
Как отвечать
Слайс это заголовок из указателя, длины и ёмкости. Одновременный append из нескольких горутин это data race: элементы теряются, заголовок читается наполовину обновлённым. Детектор гонок (go test -race) ловит это сразу.
Варианты защиты:
sync.Mutex: структура со слайсом и мьютексом, методы с pointer receiver,Lockиdefer Unlock. Базовый ответ, начинать с него;sync.RWMutex:RLockдля чтения,Lockдля записи. Выигрывает, когда чтений заметно больше, при частой записи бывает медленнее обычного мьютекса;- горутина-владелец: слайс живёт внутри одной горутины, остальные шлют ей команды через канал. Удобно, когда вокруг данных есть своя логика, но медленнее мьютекса;
- copy-on-write:
atomic.Pointer[[]int], читатели берут снимок без блокировок, писатель копирует слайс, меняет копию и подменяет указатель. Хорошо для редко меняющихся данных вроде конфигов; - без синхронизации: если слайс заранее создан нужной длины, горутины могут писать каждая в свой
results[i]. Это разные переменные, гонки нет, достаточно дождатьсяsync.WaitGroupилиerrgroup.
Отдельно скажите про чтение: метод, который возвращает s.items целиком, отдаёт наружу тот же базовый массив, и защита заканчивается на выходе из метода. Возвращайте копию через slices.Clone (Go 1.21+).
Где спотыкаются
Предлагают каналы как идиоматичную замену и пишут версию, которая блокирует сама себя: и владелец, и Append читают из одного канала, а писать в него некому. Если выбираете канал, нужен явный протокол: команда плюс канал для ответа.
Копируют структуру с мьютексом по значению, передавая её в функцию или объявляя value receiver. У каждой копии свой мьютекс, и защита исчезает. go vet ловит это проверкой copylocks.
Предлагают sync.Map. Он сделан для отображений с особым профилем доступа, к слайсам отношения не имеет.