go - programacion - Ir a concurrencia con for loop y la función anónima se comporta inesperadamente
funciones anonimas javascript (2)
Ya he encontrado una forma de que el código se comporte como yo quiero, pero me gustaría entender por qué se comporta así para que mi comprensión de la concurrencia de Go mejore.
Estaba probando sync.WaitGroup
para esperar a que terminen algunos goroutines porque planeo hacer cargas múltiples a Amazon S3 de esta manera.
Este era el código que tenía originalmente:
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func() {
fmt.Println(i)
time.Sleep(time.Second * 1)
wg.Done()
}()
}
wg.Wait()
}
Me sorprendió ver que la salida fue: 6, 6, 6, 6, 6
.
En lugar de algo como: 2, 4, 1, 5, 3
.
Como el ciclo ni siquiera va a 6, esto no tiene sentido para mí. Más tarde pasé la variable i
a la función anónima como argumento y luego se comportó como esperaba.
¿Por qué pasó esto? No lo entiendo
Esto está cubierto en la pregunta frecuente : ¿Qué sucede con los cierres que se ejecutan como goroutines?
En este caso, ninguno de los goroutines se programa hasta que complete el bucle for. Para que el bucle for se rompa, no debe ser menor o igual a 5, por lo tanto, es 6 en ese punto. Cuando se ejecutan los goroutines, cada uno imprime el valor de la variable individual i
que se captura en los cierres.
Cuando pasa i
como argumento a la función, copia el valor actual en una nueva variable, capturando el valor en ese momento.
Para responder a su pregunta, debe pasar a su func
para que cada rutina tenga su propia copia del valor de i
.
Entonces tu código debería verse así
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(i int) {
fmt.Println(i)
time.Sleep(time.Second * 1)
wg.Done()
}(i)
}
wg.Wait()
}
Escribí esta función de utilidad para ayudar a paralelizar un grupo de funciones:
import "sync"
// Parallelize parallelizes the function calls
func Parallelize(functions ...func()) {
var waitGroup sync.WaitGroup
waitGroup.Add(len(functions))
defer waitGroup.Wait()
for _, function := range functions {
go func(copy func()) {
defer waitGroup.Done()
copy()
}(function)
}
}
Entonces en tu caso, podríamos hacer esto
func main() {
functions := []func(){}
for i := 1; i <= 5; i++ {
function := func(i int) func() {
return func() {
fmt.Println(i)
}
}(i)
functions = append(functions, function)
}
Parallelize(functions...)
fmt.Println("Done")
}
Si desea utilizar la función Parallelize, puede encontrarla aquí https://github.com/shomali11/util