← Назад к вопросам

Что такое data race и race condition? В чем разница?

2.0 Middle🔥 152 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Data Race (Гонка данных) и Race Condition (Состояние гонки)

Это два фундаментальных, но различных понятия в конкурентном программировании, которые часто путают. Хотя они связаны, их природа и последствия отличаются.

Что такое Data Race (Гонка данных)?

Гонка данных — это конкретная ситуация на уровне памяти, когда два или более потока (горутины) одновременно обращаются к одной и той же ячейке памяти, и как минимум одно из обращений является записью, при отсутствии синхронизации доступа.

Это состояние неопределённости выполнения на низком уровне. Компилятор, процессор и среда выполнения могут переставлять операции чтения/записи для оптимизации, что при одновременном доступе приводит к непредсказуемым результатам.

Ключевые характеристики data race:

  • Это событие, которое либо происходит, либо нет.
  • Относится к низкоуровневой работе с памятью.
  • Может быть обнаружено инструментами (в Go: go run -race, go test -race).
  • Часто (но не всегда) является причиной race condition.

Пример data race в Go:

package main

import "sync"

func main() {
    var counter int
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter++ // ЧТЕНИЕ-МОДИФИКАЦИЯ-ЗАПИСЬ без синхронизации -> DATA RACE!
        }()
    }
    wg.Wait()
    println(counter) // Результат будет непредсказуемым, почти всегда < 1000
}

Что такое Race Condition (Состояние гонки)?

Race condition — это более широкое логическое ошибка в дизайне программы, при которой корректность выполнения (правильность результата) зависит от относительного порядка или времени выполнения операций в нескольких горутинах, который разработчик не контролирует. Это нарушение инвариантов программы.

Состояние гонки не обязательно включает одновременную запись в одну память. Оно может возникать из-за неправильного порядка операций, даже если каждая из них по отдельности защищена мьютексами.

Ключевые характеристики race condition:

  • Это неправильное поведение (логическая ошибка), а не конкретное событие.
  • Относится к высокоуровневой логике и семантике программы.
  • Его сложнее обнаружить автоматически, часто требуется анализ логики.
  • Может существовать даже при отсутствии data race (если есть корректная синхронизация доступа к памяти, но логика остаётся неверной).

Пример race condition БЕЗ data race в Go:

package main

import (
    "sync"
    "time"
)

var (
    mu      sync.Mutex
    ready   bool
    message string
)

// Горутина A
func initMessage() {
    mu.Lock()
    message = "Hello, World!" // (1) Запись защищена мьютексом
    time.Sleep(2 * time.Second)
    ready = true              // (2) Запись защищена мьютексом
    mu.Unlock()
}

// Горутина B
func useMessage() {
    mu.Lock()
    if ready {                // (3) Чтение защищено мьютексом
        println(message)      // (4) Чтение защищено мьютексом
    }
    mu.Unlock()
}

func main() {
    go initMessage()
    go useMessage()
    time.Sleep(3 * time.Second)
}

Здесь нет data race — весь доступ к разделяемым переменным (ready, message) защищён мьютексом mu. Однако здесь есть race condition (логическая ошибка). Если горутина useMessage захватит мьютекс и проверит ready до того, как горутина initMessage установит его в true, то сообщение не будет выведено, хотя логика программы, возможно, подразумевала обратное. Результат зависит от порядка выполнения шагов (1)-(4).

Сравнительная таблица

КритерийData RaceRace Condition
СущностьКонкретное событие нарушения доступа к памяти.Логическая ошибка в дизайне (семантическая проблема).
УровеньНизкоуровневый (память, процессорные инструкции).Высокоуровневый (логика приложения).
ПричинаОдновременный доступ на запись без синхронизации.Неправильные допущения о порядке событий.
ОбнаружениеМожет быть выявлено автоматически (Go race detector).Часто требует анализа кода человеком, сложных тестов.
СвязьЧасто является симптомом или причиной race condition.Может существовать как с data race, так и без неё.

В чем разница? Итог

Проще всего запомнить так:

  • Data Race — это когда два потока "дерутся" за одну ячейку памяти. Это нарушение правил безопасности доступа.
  • Race Condition — это когда результат работы программы зависит от того, кто "прибежал первым". Это нарушение логической корректности.

Важнейший вывод для разработчика на Go: Использование -race флага и детектора гонок помогает отловить опасные data races, что критически важно. Однако это не гарантирует отсутствия race conditions в вашей программе. Устранение data race (путем добавления мьютексов или каналов) — это лишь первый, технический шаг. Далее необходимо проектировать логику конкурентных взаимодействий так, чтобы она была корректной при любом порядке выполнения операций. Для этого используются примитивы синхронизации (sync.Mutex, sync.WaitGroup, sync.Once), каналы и принципы, такие как "Share memory by communicating" (разделяй память через общение), которым следует Go.

Что такое data race и race condition? В чем разница? | PrepBro