Ответ
Одновременная запись в map
из нескольких горутин без внешних механизмов синхронизации приведет к состоянию гонки (data race).
Тип map
в Go не является потокобезопасным для записи. Последствия состояния гонки могут быть следующими:
- Паника (panic): Наиболее вероятный исход. Среда выполнения Go содержит встроенный детектор гонок для
map
и аварийно завершит программу с ошибкойfatal error: concurrent map writes
. - Повреждение данных: Внутренняя структура
map
может быть повреждена, что приведет к потере данных или несогласованному состоянию. - Непредсказуемое поведение: Программа может продолжить работу, но с неверными данными в
map
.
Пример опасного кода:
m := make(map[int]int)
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m[i] = i // Опасно! Возникнет паника.
}(i)
}
wg.Wait()
Как решить проблему?
Для безопасной работы с map
в конкурентной среде необходимо использовать примитивы синхронизации:
-
sync.Mutex
: Самый распространенный способ. Блокировка защищаетmap
на время операции записи (или чтения).var ( m = make(map[int]int) mu sync.Mutex ) mu.Lock() // Захватываем блокировку m[key] = value mu.Unlock() // Освобождаем
-
sync.RWMutex
: Более производительный вариант, если количество чтений значительно превышает количество записей. Позволяет множеству горутин читать одновременно.var rwMu sync.RWMutex // Для записи rwMu.Lock() m[key] = value rwMu.Unlock() // Для чтения rwMu.RLock() _ = m[key] rwMu.RUnlock()
-
sync.Map
: Специализированный тип, оптимизированный для сценариев, когда ключ записывается один раз, а затем многократно читается, или когда разные горутины работают с разными наборами ключей. Имеет другой API (Store
,Load
,Delete
).