Ответ
При одновременной записи (или записи и чтении) в map
из нескольких горутин произойдет состояние гонки (race condition), которое с высокой вероятностью приведет к панике (fatal error: concurrent map writes
) и аварийному завершению программы.
Почему это происходит?
Стандартная map
в Go не является потокобезопасной. Это сделано намеренно для максимальной производительности в однопоточных сценариях. Внутренние операции с map
, такие как перехеширование или добавление элемента, могут изменять её структуру (например, перемещать бакеты). Если одна горутина начнет запись в момент, когда другая изменяет эту структуру, целостность данных будет нарушена.
Пример опасного кода:
func main() {
m := make(map[int]string)
// Две горутины одновременно пишут в одну и ту же мапу
go func() {
for i := 0; ; i++ {
m[i] = "a"
}
}()
go func() {
for i := 0; ; i++ {
m[i] = "b"
}
}()
// Программа упадет с паникой
select {}
}
Как решить проблему?
Для безопасной работы с map
в конкурентной среде используются примитивы синхронизации:
-
sync.RWMutex
(предпочтительный способ): Позволяет множественные одновременные чтения, но запись делает эксклюзивной. Это идеальный вариант, если у вас гораздо больше чтений, чем записей.var mu sync.RWMutex var m = make(map[int]int) // Безопасная запись mu.Lock() m[1] = 42 mu.Unlock() // Безопасное чтение mu.RLock() _ = m[1] mu.RUnlock()
-
sync.Map
: Специализированный тип, оптимизированный для двух конкретных случаев:- Когда ключ записывается один раз, а затем многократно читается.
- Когда разные горутины работают с разными, непересекающимися наборами ключей.
В общих случаях
sync.Map
может быть медленнее, чемmap
сRWMutex
.