среда, 27 мая 2020 г.

Итерационные переменные и замыкания в Golang

Почему эта программа

func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func() {
            fmt.Print(i)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println()
}

печатает

55555

(WaitGroup ожидает завершения набора goroutine.)

Ответ

Существует гонка данных: переменная i совместно используется шестью (6) goroutine.

Гонка данных происходит, когда две goroutine обращаются к одной и той же переменной одновременно, и по крайней мере одно из обращений является записью.

Чтобы избежать этого, используйте локальную переменную и передайте число в качестве параметра при запуске goroutine.

func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(n int) { // Используем локальную переменную.
            fmt.Print(n)
            wg.Done()
        }(i)
    }
    wg.Wait()
    fmt.Println()
}

Пример вывода:

40123

Также возможно избежать этой гонки данных, все еще используя замыкание, но тогда мы должны позаботиться об использовании уникальной переменной для каждой goroutine.

func main() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        n := i // Создаем уникальную переменную для каждого замыкания.
        go func() {
            fmt.Print(n)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println()
}


Читайте также:


Комментариев нет:

Отправить комментарий