Почему этот count++ в 1000 горутинах не доходит надёжно до 1000 и как это исправить?
Эта программа увеличивает общий count из 1000 горутин и печатает итог. Она не выводит надёжно 1000.
var count int
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() { defer wg.Done(); count++ }()
}
wg.Wait()
fmt.Println(count)
Найдите и исправьте ошибку.
count++ — это read-modify-write, не атомарно. Много горутин читают одно значение, увеличивают и пишут обратно, поэтому инкременты теряются, а итог недетерминирован — go run -race ловит data race. Исправление — mutex вокруг count++ или atomic.AddInt64(&count, 1) с count типа int64.
- ✗Считать
count++одной атомарной инструкцией, а не read-modify-write - ✗Винить
WaitGroupвместо несинхронизированной общей записи - ✗Думать, что
time.Sleepили сброс кэша исправляет data race
- →Почему
atomic.AddInt64дешевле mutex для одного счётчика? - →Что именно наблюдает детектор
-race, чтобы пометить это?
Найдите ошибку
var count int
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() { defer wg.Done(); count++ }()
}
wg.Wait()
fmt.Println(count) // не надёжно 1000
Почему не 1000
count++ выглядит как одна операция, но на деле это три: прочитать count, прибавить 1, записать обратно. Когда тысяча горутин делают это одновременно без синхронизации, две из них могут прочитать одно и то же значение, прибавить 1 и записать — один инкремент потерян. Итог получается случайным и обычно меньше 1000.
Это классический data race; go run -race укажет на конкурентный доступ к count.
✅ Исправления:
// 1) mutex
var mu sync.Mutex
go func() { defer wg.Done(); mu.Lock(); count++; mu.Unlock() }()
// 2) атомарный счётчик (быстрее для одной переменной)
var count int64
go func() { defer wg.Done(); atomic.AddInt64(&count, 1) }()
⚠️ time.Sleep не лечит гонку — он лишь маскирует её на конкретном прогоне.