Приведите пример ошибки синхронизации в многопоточном коде на Go при работе с мапой без мьютекса и опишите, как race detector и sync.Map или мьютекс помогут решить проблему

24 Ноя в 12:16
1 +1
0
Ответы
1
Пример ошибки и краткие способы исправления.
Баг (несинхронизированный доступ к мапе):
```go
package main
import (
"fmt"
"time"
)
var m = make(map[int]int)
func main() {
for i := 0; i < 1000; i++ {
go func(i int) { m[i] = i }(i) // запись
go func(i int) { _ = m[i] }(i) // чтение
}
time.Sleep(time.Second)
fmt.Println("done")
}
```
При запуске этот код часто падает с `fatal error: concurrent map writes` или ведёт к data race.
Почему это проблема:
- Встроенная `map` в Go не потокобезопасна: одновременные запись и чтение/запись без синхронизации приводят к гонке и возможному краху.
Как race detector помогает:
- Запустите с флагом race: `go run -race` или `go test -race`. Ран-датектор инструментирует доступы к памяти и при обнаружении гонки выдаст отчёт с трассировкой стека, указывая места конфликтующих обращений — это помогает локализовать проблему.
Исправление 1 — мьютекс (правильно для общего случая):
```go
package main
import (
"fmt"
"sync"
"time"
)
var (
mu sync.RWMutex
m = make(map[int]int)
)
func main() {
for i := 0; i < 1000; i++ {
go func(i int) {
mu.Lock()
m[i] = i
mu.Unlock()
}(i)
go func(i int) {
mu.RLock()
_ = m[i]
mu.RUnlock()
}(i)
}
time.Sleep(time.Second)
fmt.Println("done")
}
```
- `sync.Mutex` или `sync.RWMutex` обеспечивает атомарность и упорядоченность доступов; RWMutex позволяет конкурентным чтениям идти параллельно.
Исправление 2 — sync.Map (готовое потокобезопасное хранилище):
```go
package main
import (
"fmt"
"sync"
"time"
)
var m sync.Map
func main() {
for i := 0; i < 1000; i++ {
go func(i int) { m.Store(i, i) }(i)
go func(i int) { m.Load(i) }(i)
}
time.Sleep(time.Second)
fmt.Println("done")
}
```
- `sync.Map` безопасна для конкурентного доступа без внешней блокировки; подходит для кейсов с частыми чтениями и редкими модификациями, но имеет другие характеристики производительности и API (ключ/значение — `interface{}`).
Краткие рекомендации:
- Для простых случаев используйте `sync.Mutex`/`sync.RWMutex`.
- Для специализированных сценариев с высокой долей чтений рассмотрите `sync.Map`.
- Всегда проверяйте подозрительные участки кода с `-race` перед релизом.
24 Ноя в 12:25
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир