Considere el siguiente código Go (también en el Go Playground ):
package main
import "fmt"
import "time"
func main() {
for _, s:= range []string{"foo", "bar"} {
x:= s
func() {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}()
}
fmt.Println()
for _, s:= range []string{"foo", "bar"} {
x:= s
go func() {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}()
}
time.Sleep(time.Second)
}
Este código produce el siguiente resultado:
s: foo
x: foo
s: bar
x: bar
s: bar
x: foo
s: bar
x: bar
Asumiendo que esto no es un error extraño del compilador, tengo curiosidad por qué a) el valor de s se interpreta de manera diferente en la versión goroutine que en la llamada de función normal yb) y por qué asignarlo a una variable local dentro del ciclo funciona en ambos casos.
Solución del problema
Los cierres en Go tienen un alcance léxico. Esto significa que cualquier variable a la que se haga referencia dentro del cierre desde el ámbito "externo" no es una copia sino una referencia. Un for
bucle en realidad reutiliza la misma variable varias veces, por lo que está introduciendo una condición de carrera entre la lectura/escritura de la s
variable.
Pero x
está asignando una nueva variable (con :=
) y copiando s
, lo que da como resultado que ese sea el resultado correcto cada vez.
En general, es una buena práctica pasar cualquier argumento que desee para que no tenga referencias. Ejemplo:
for _, s:= range []string{"foo", "bar"} {
x:= s
go func(s string) {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}(s)
}
No hay comentarios.:
Publicar un comentario