Почему запись во встроенную map из многих горутин падает и как это исправить?
Эта программа запускает 100 горутин, каждая из которых пишет одну запись в общую встроенную map. Она падает в runtime вместо штатного завершения.
func main() {
m := map[int]int{}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) { defer wg.Done(); m[i] = i }(i)
}
wg.Wait()
}
Найдите и исправьте ошибку.
Встроенные map не безопасны для конкурентного доступа. Конкурентные записи аварийно завершают программу с fatal error: concurrent map writes — даже чтение с записью одновременно может упасть. Исправление — защищать каждый доступ sync.Mutex/RWMutex либо использовать sync.Map под конкуренцией.
- ✗Считать встроенную map безопасной для конкурентных записей, если ключи различны
- ✗Думать, что это поправимый data race, а не фатальная ошибка runtime
- ✗Считать, что преразмер map устраняет нужду в синхронизации
- →Когда
sync.Mapпредпочтительнее обычной map подMutex? - →Почему даже одновременные чтение и запись могут упасть, а не только две записи?
Найдите ошибку
func main() {
m := map[int]int{}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) { defer wg.Done(); m[i] = i }(i)
}
wg.Wait()
}
Почему падает
Встроенная map в Go не предназначена для конкурентного использования — даже если каждая горутина пишет свой ключ. Внутри runtime есть флаг «идёт запись»; если другая горутина начинает запись (или чтение) одновременно, runtime обнаруживает это и аварийно завершает программу:
fatal error: concurrent map writes
Это не поправимый data race, а немедленное фатальное падение. Различие ключей не помогает: общая структура бакетов одна, и параллельная запись её повреждает.
✅ Исправления:
// 1) защитить mutex
var mu sync.Mutex
go func(i int){ defer wg.Done(); mu.Lock(); m[i] = i; mu.Unlock() }(i)
// 2) sync.Map для конкурентных нагрузок
var m sync.Map
go func(i int){ defer wg.Done(); m.Store(i, i) }(i)