Проблема гонки данных при запуске горутин в цикле

Сначала исправьте двоеточие в for на точку с запятой. Гонка по общей переменной относится к старой семантике Go; в Go 1.22+ переменная, объявленная таким циклом, отдельная для каждой итерации.

Короткий ответ

Что ответить на собеседовании

Сначала исправьте двоеточие в for на точку с запятой. Гонка по общей переменной относится к старой семантике Go; в Go 1.22+ переменная, объявленная таким циклом, отдельная для каждой итерации.

Подробный разбор

Ответ с пояснениями

Условие

В этом примере показана типичная ошибка, когда несколько горутин одновременно используют переменную цикла. Какой именно баг присутствует в коде и какие способы позволяют избавиться от него?

func main(){
    for i := 0; i < 10: i++ {
        go func(){
            fmt.Println(i)
        }()
    }
}

Что происходит:

  • Цикл использует одну и ту же переменную i для всех итераций. К моменту выполнения анонимной функции значение i уже может измениться, поэтому горутины могут вывести одинаковые или «пропущенные» числа.
  • Кроме того, в заголовке цикла стоит двоеточие вместо точки с запятой, что делает код синтаксически неверным.

Как исправить:

  1. Передать текущий индекс в функцию как параметр. Это создаст отдельную копию значения для каждой горутины.
func main() {
    for idx := 0; idx < 10; idx++ {
        go func(v int) {
            fmt.Println(v)
        }(idx)
    }
}
  1. Локальная переменная внутри тела цикла.
func main() {
    for i := 0; i < 10; i++ {
        v := i // отдельная копия
        go func() {
            fmt.Println(v)
        }()
    }
}
  1. Синхронизация завершения горутин, например, через sync.WaitGroup, чтобы гарантировать вывод всех чисел до завершения программы.
func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(v int) {
            defer wg.Done()
            fmt.Println(v)
        }(i)
    }
    wg.Wait()
}

Эти приёмы устраняют состояние гонки и делают программу предсказуемой.

Ответ

Сначала исправьте двоеточие в for на точку с запятой. Гонка по общей переменной относится к старой семантике Go; в Go 1.22+ переменная, объявленная таким циклом, отдельная для каждой итерации.

Передача индекса аргументом остаётся понятным переносимым решением. WaitGroup из третьего варианта необходим для гарантированного завершения всех горутин, но не задаёт порядок печати. Первые два исправления без ожидания могут ничего не вывести до выхода main. Если i объявлена снаружи и цикл использует присваивание, общая переменная остаётся общей и в новых версиях.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее