问题
I'd like to add concurrency for iterating nested loops but having a trouble. What's wrong with this example usage of 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()
I'm getting
PRS AAC PRS AAC PRS AAC PRS AAC PRS AAC PRS AAC
So as you may see it's skipping first elements of both array and iterating only the last ones. Any ideas how to fix this behavior?
回答1:
It's a closure problem. You need to pass values to your goroutine inside the loop, like this:
for _, originIata := range originCities {
for _, destinationIata := range destinationCities {
go func (originIata, destinationIata string) {
fmt.Println(originIata)
fmt.Println(destinationIata)
wg.Done()
}(originIata, destinationIata)
}
}
回答2:
As has been posted, one can pass the values as function parameters to the goroutine function.
Or one can use the technique of creating explicit variables within the scope of the loops. For simplicity, you can reuse the same variable name. This ensures the goroutine references the for loop's closure value (and not the externally-scoped dynamic value you were experiencing):
for _, originIata := range originCities {
originIata := originIata // here
for _, destinationIata := range destinationCities {
destinationIata := destinationIata // here
go func () {
fmt.Println(originIata)
fmt.Println(destinationIata)
wg.Done()
}()
}
}
Note: the above fix will only work, if the copies are done outside the goroutine function.
Edit: use go tools like go vet
and go's race-detector to help catch these gotcha type bugs.
For example, the go playground (as well as popular editors like VScode) run go vet
by default e.g.
https://play.golang.org/p/JhALssCu2-T
But note, do not rely on go vet
as a security blanket. In the playground above, it does not catch the outer o
potential race condition.
You can build your executable with a data-race detector (tl;dr; go build -race
; use this for testing and not production - as it performs slower and has something like a 8K go-routine limit).
The race-detector will only catch data-races issues at runtime. So use it judiciously, as it is not a code-flow analyzer and thus cannot predict any future potential code execution issues.
来源:https://stackoverflow.com/questions/58533613/sync-waitgroup-and-nested-loops