суббота, 16 февраля 2019 г.

Go FAQ: Что происходит с замыканиями, выполняемыми как go-процедуры?

Некоторая путаница может возникнуть при использовании замыканий с конкурентностью. Рассмотрим следующую программу:

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

    // ждем завершения всех go-процедур перед выходом
    for _ = range values {
        <-done
    }
}

Можно ошибочно ожидать, что a,b,c будет отображаться как результат. Вместо этого вы, вероятно, увидите c,c,c. Это потому, что каждая итерация цикла использует один и тот же экземпляр переменной v, поэтому каждое замыкание разделяет эту единственную переменную. Когда замыкание выполняется, оно печатает значение v в момент выполнения fmt.Println, но v, возможно, был изменен с момента запуска программы. Чтобы помочь обнаружить эту и другие проблемы, прежде чем они возникнут, запустите go vet.

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

for _, v := range values {
    go func(u string) {
        fmt.Println(u)
        done <- true
    }(v)
}

В этом примере значение v передается в качестве аргумента анонимной функции. Это значение затем доступно внутри функции как переменная u.

Еще проще создать новую переменную, используя стиль объявления, который может казаться странным, но отлично работает в Go:

for _, v := range values {
    v := v // создаем новую 'v'.
    go func() {
        fmt.Println(v)
        done <- true
    }()
}

Это поведение языка, что не происходит определения новой переменной для каждой итерации, возможно, было ошибкой в ​​ретроспективе. Это может быть решено в более поздней версии, но, для совместимости, не может измениться в версии Go 1.


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


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

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