Ответ
Состояние гонки (Race Condition) — это ошибка проектирования многопоточной системы, при которой результат работы зависит от непредсказуемого порядка выполнения операций в разных горутинах.
Проблема возникает, когда две или более горутины одновременно обращаются к одной и той же области памяти (общему ресурсу), и хотя бы одна из них выполняет запись.
Пример
Классический пример — инкремент счетчика из нескольких горутин без синхронизации. Операция counter++
неатомарна и состоит из трех шагов: чтение, изменение, запись. Другая горутина может прочитать значение до того, как первая его запишет, что приведет к потере инкремента.
var counter int
func increment() {
counter++ // Небезопасная операция
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println(counter) // Результат будет < 1000
}
Способы предотвращения в Go
-
Мьютексы (
sync.Mutex
): Самый распространенный способ. Мьютекс позволяет только одной горутине получить доступ к критической секции кода.var mu sync.Mutex mu.Lock() counter++ mu.Unlock()
-
Каналы: Использование каналов для передачи данных между горутинами, что позволяет избежать общего доступа к памяти. Например, одна горутина может владеть данными и обрабатывать запросы на их изменение, получаемые из канала.
-
Атомарные операции (
sync/atomic
): Для простых операций, таких как инкремент, декремент илиCompareAndSwap
, пакетatomic
предоставляет аппаратно-оптимизированные функции, которые выполняются без прерываний. Это эффективнее, чем использование мьютекса.atomic.AddInt64(&counter, 1)
Обнаружение
Go имеет встроенный детектор гонок. Чтобы его использовать, нужно запустить программу с флагом -race
:
go run -race main.go