В Go map по умолчанию не является потокобезопасной структурой данных. Одновременная запись или изменение map из разных горутин приводит к гонкам и паникам типа "concurrent map writes". Для защиты map обычно используют sync.Mutex, sync.RWMutex или специальный тип sync.Map из стандартной библиотеки, который реализует безопасные атомарные операции.
Удалять элементы из map можно через delete(map, key), однако при итерации по map (через range) возникнут нюансы:
Пример потокобезопасной работы с map:
type SafeMap struct { mu sync.RWMutex m map[string]int } func (s *SafeMap) Load(key string) (int, bool) { s.mu.RLock() v, ok := s.m[key] s.mu.RUnlock() return v, ok } func (s *SafeMap) Store(key string, value int) { s.mu.Lock() s.m[key] = value s.mu.Unlock() }
Вопрос: Можно ли безопасно удалять элементы из map во время range-итерации по этой же map?
Ответ: Да, только если удалять ключ, который уже был выдан range-итерацией. Но нельзя при этом параллельно модифицировать map из других горутин: это приведёт к гонке.
m := map[string]int{"a": 1, "b": 2, "c": 3} for k := range m { delete(m, k) // безопасно! (если делаем только из этой горутины) }
История
Один из сервисов мониторинга вёл статистику по map и обновлялся сразу из нескольких горутин (счётчики метрик). В пиковый момент возникли паники "concurrent map writes", сервис отключался, данные терялись. Решение: добавить mutex или использовать sync.Map вместо обычной map.
История
В процессе миграции данных кто-то решил ускорить очистку большого map с помощью параллельных горутин, каждая из которых удаляла свою часть ключей через range. Как итог — постоянные data race и непредсказуемые крэши. После миграции пришлось вернуться к последовательной очистке или блокировать доступ на время range.
История
В цикле range по map разработчик добавлял новые данные в ту же map (например, формировал список смежных узлов для графа). Оказалось, что новые ключи могут быть проигнорированы в текущем проходе range, что привело к неполной обработке графа. Баг был обнаружен только при полном тестировании редких кейсов. После исправления алгоритм был переписан с использованием отдельной очереди добавлений.