sync.WaitGroup и вложенные циклы - PullRequest
0 голосов
/ 24 октября 2019

Я хотел бы добавить параллелизм для итерации вложенных циклов, но возникли проблемы. Что не так с этим примером использования sync.WaitGroup?

originCities := [3]string{"LED", "MOW", "PRS"}
destinationCities := [2]string{"UKT", "AAC"}

wg := &sync.WaitGroup{}
wg.Add(len(originCities) * len(destinationCities))

for _, originIata := range originCities {
    for _, destinationIata := range destinationCities {
        go func () {
            fmt.Println(originIata)
            fmt.Println(destinationIata)
            wg.Done()
        }()
    }
}
wg.Wait()

Я получаю

PRS AAC PRS AAC PRS AAC PRS AAC PRS AAC PRS AAC

Так что, как вы можете видеть, это пропускпервые элементы как массива, так и повторяющиеся только последние. Есть идеи, как исправить это поведение?

Ответы [ 2 ]

4 голосов
/ 24 октября 2019

Как было объявлено, можно передать значения в качестве параметров функции в функцию goroutine.

Или можно использовать метод создания явных переменных в области действия циклов. Для простоты вы можете использовать одно и то же имя переменной. Это гарантирует, что подпрограмма ссылается на значение закрытия цикла for (а не на динамическое значение внешней области действия, которое вы испытывали):

for _, originIata := range originCities {
    originIata := originIata // here
    for _, destinationIata := range destinationCities {
        destinationIata := destinationIata // here
        go func () {
            fmt.Println(originIata)
            fmt.Println(destinationIata)
            wg.Done()
        }()
    }
}

Примечание: указанное выше исправление будет работать, только если копии сделаны за пределами функции goroutine.


Редактировать: использовать инструменты go, такие как go vet, и детектор гонки go , чтобы помочь поймать эти gotcha ошибки типа.

Например, игровая площадка go (а также популярные редакторы, такие как VScode) по умолчанию запускают go vet, например,

https://play.golang.org/p/JhALssCu2-T

Но обратите внимание, не не полагается на go vet в качестве защитного покрытия. На игровой площадке выше не ловит внешнее o потенциальное состояние гонки.

Вы можете создать свой исполняемый файл с помощью детектора гонки данных (tl;dr; go build -race; используйте это для тестирования, а не для производства - так как он работает медленнее и имеет что-то вроде предела стандартной процедуры 8K).

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

2 голосов
/ 24 октября 2019

Это проблема закрытия. Вам нужно передать значения вашей подпрограмме внутри цикла, например:

for _, originIata := range originCities {
    for _, destinationIata := range destinationCities {
        go func (originIata, destinationIata string) {
            fmt.Println(originIata)
            fmt.Println(destinationIata)
            wg.Done()
        }(originIata, destinationIata)
    }
}
...