close - golang run
No hay salida de goroutine en Go (3)
Mientras
SayHello()
ejecuta como se esperaba, la gorutina no imprime nada.
package main
import "fmt"
func SayHello() {
for i := 0; i < 10 ; i++ {
fmt.Print(i, " ")
}
}
func main() {
SayHello()
go SayHello()
}
Alternativamente (a la respuesta de icza) puede usar
WaitGroup
del paquete de
sync
y la función anónima para evitar alterar
SayHello
original.
package main
import (
"fmt"
"sync"
)
func SayHello() {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
}
func main() {
SayHello()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
SayHello()
}()
wg.Wait()
}
Para imprimir números, ejecute simultáneamente cada declaración de impresión en una rutina separada como la siguiente
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(fnScopeI int) {
defer wg.Done()
// next two strings are here just to show routines work simultaneously
amt := time.Duration(rand.Intn(250))
time.Sleep(time.Millisecond * amt)
fmt.Print(fnScopeI, " ")
}(i)
}
wg.Wait()
}
Como otros han mencionado, el programa Go sale cuando la función
main
regresa.
Una opción es usar algo como
sync.WaitGroup
para esperar a las otras gorutinas que
main
ha generado antes de regresar de
main
.
Otra opción es llamar a
runtime.Goexit()
en
main
.
Desde el
godoc
:
Goexit termina la rutina que lo llama. Ninguna otra gorutina se ve afectada. Goexit ejecuta todas las llamadas diferidas antes de terminar la rutina. Debido a que Goexit no es pánico, cualquier llamada de recuperación en esas funciones diferidas devolverá nulo.
Llamar a Goexit desde la goroutina principal termina esa goroutina sin que la función principal regrese. Dado que func main no ha regresado, el programa continúa la ejecución de otras gorutinas. Si todas las otras gorutinas salen, el programa se bloquea.
Esto permite que la rutina principal deje de ejecutarse mientras las rutinas en segundo plano continúan ejecutándose. Por ejemplo:
package main
import (
"fmt"
"runtime"
"time"
)
func f() {
for i := 0; ; i++ {
fmt.Println(i)
time.Sleep(10 * time.Millisecond)
}
}
func main() {
go f()
runtime.Goexit()
}
Esto puede ser más limpio que bloquear para siempre en la función principal, especialmente para programas que son infinitos. Una desventaja es que si todas las rutinas de un proceso regresan o salen (incluida la principal), Go detectará esto como un error y pánico:
fatal error: no goroutines (main called runtime.Goexit) - deadlock!
Para evitar esto, al menos una gorutina debe llamar a
os.Exit
antes de que regrese.
Llamar a
os.Exit(0)
inmediatamente termina el programa e indica que lo hizo sin error.
Por ejemplo:
package main
import (
"fmt"
"os"
"runtime"
"time"
)
func f() {
for i := 0; i < 10; i++ {
fmt.Println(i)
time.Sleep(10 * time.Millisecond)
}
os.Exit(0)
}
func main() {
go f()
runtime.Goexit()
}
Cuando finaliza su función
main()
, su programa también finaliza.
No espera a que otras gorutinas terminen.
Citando desde el Go Idioma Especificación: Ejecución del programa :
La ejecución del programa comienza inicializando el paquete principal y luego invocando la función
main
. Cuando la invocación de esa función regresa, el programa sale. No espera a que se completen otras gorutinas (nomain
).
Vea esta respuesta para más detalles.
Debe decirle a su función
main()
que espere a que la función
SayHello()
inicie como una rutina completa.
Puede sincronizarlos con canales, por ejemplo:
func SayHello(done chan int) {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
if done != nil {
done <- 0 // Signal that we''re done
}
}
func main() {
SayHello(nil) // Passing nil: we don''t want notification here
done := make(chan int)
go SayHello(done)
<-done // Wait until done signal arrives
}
Otra alternativa es señalar la finalización cerrando el canal:
func SayHello(done chan struct{}) {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
if done != nil {
close(done) // Signal that we''re done
}
}
func main() {
SayHello(nil) // Passing nil: we don''t want notification here
done := make(chan struct{})
go SayHello(done)
<-done // A receive from a closed channel returns the zero value immediately
}
Notas:
Según sus ediciones / comentarios: si desea que las 2 funciones de ejecución de
SayHello()
impriman números "mixtos" al azar: no tiene garantía de observar este comportamiento.
Nuevamente, vea la
respuesta antes mencionada
para más detalles.
El
modelo Go Memory
solo garantiza que ciertos eventos sucedan antes que otros eventos, no tiene garantía de cómo se ejecutan 2 goroutines concurrentes.
Puede experimentar con él, pero sepa que el resultado no será determinista. Primero debes habilitar múltiples goroutines activos para ser ejecutados con:
runtime.GOMAXPROCS(2)
Y segundo, primero debe iniciar
SayHello()
como una rutina porque su código actual primero ejecuta
SayHello()
en la rutina principal y solo una vez que termina, comienza el otro:
runtime.GOMAXPROCS(2)
done := make(chan struct{})
go SayHello(done) // FIRST START goroutine
SayHello(nil) // And then call SayHello() in the main goroutine
<-done // Wait for completion