Cómo detener una goroutine
Pregunta
Tengo una goroutina que llama a un método y pasa el valor devuelto en un canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
¿Cómo detengo una goroutina así?
Solución
EDITAR: Escribí esta respuesta a toda prisa, antes de darme cuenta de que su pregunta se trata de enviar valores a un chan dentro de una goroutina. El siguiente enfoque se puede usar con un chan adicional como se sugirió anteriormente, o utilizando el hecho de que el chan que ya tiene es bidireccional, puede usar solo el ...
Si su Goroutine existe únicamente para procesar los elementos que salen del Chan, puede hacer uso de la construcción "cerrada" y el formulario de recepción especial para los canales.
Es decir, una vez que hayas terminado de enviar artículos al chan, lo cierras. Luego, dentro de su goroutine obtendrá un parámetro adicional al operador de recepción que muestra si el canal ha sido cerrado.
Aquí hay un ejemplo completo (el grupo de espera se usa para asegurarse de que el proceso continúe hasta que se complete la goroutina):
package main
import "sync"
func main() {
var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func() {
for {
foo, ok := <- ch
if !ok {
println("done")
wg.Done()
return
}
println(foo)
}
}()
ch <- 1
ch <- 2
ch <- 3
close(ch)
wg.Wait()
}
Otros consejos
Por lo general, pasa el canal de señal Goroutine A (posiblemente separado). Ese canal de señal se usa para impulsar un valor cuando desea que la goroutina se detenga. Las encuestas de Goroutine que canalizan regularmente. Tan pronto como detecta una señal, se deja.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
No puedes matar una goroutina desde afuera. Puede indicar que una goroutina deje de usar un canal, pero no hay manejo de Goroutines para hacer ningún tipo de meta gestión. Las goroutinas están destinadas a resolver los problemas de manera cooperativa, por lo que matar uno que se porta mal casi nunca sería una respuesta adecuada. Si desea un aislamiento para la robustez, probablemente desee un proceso.
En general, puede crear un canal y recibir una señal de parada en la Goroutine.
Hay dos formas de crear canal en este ejemplo.
canal
contexto. En el ejemplo lo demo
context.WithCancel
La primera demostración, usar channel
:
package main
import "fmt"
import "time"
func do_stuff() int {
return 1
}
func main() {
ch := make(chan int, 100)
done := make(chan struct{})
go func() {
for {
select {
case ch <- do_stuff():
case <-done:
close(ch)
return
}
time.Sleep(100 * time.Millisecond)
}
}()
go func() {
time.Sleep(3 * time.Second)
done <- struct{}{}
}()
for i := range ch {
fmt.Println("receive value: ", i)
}
fmt.Println("finish")
}
La segunda demostración, usar context
:
package main
import (
"context"
"fmt"
"time"
)
func main() {
forever := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
select {
case <-ctx.Done(): // if cancel() execute
forever <- struct{}{}
return
default:
fmt.Println("for loop")
}
time.Sleep(500 * time.Millisecond)
}
}(ctx)
go func() {
time.Sleep(3 * time.Second)
cancel()
}()
<-forever
fmt.Println("finish")
}
Sé que esta respuesta ya ha sido aceptada, pero pensé en tirar mis 2cents. Me gusta usar el tumba paquete. Básicamente es un canal de dejar de lado, pero también hace cosas buenas como dejar cualquier error. La rutina bajo control todavía tiene la responsabilidad de verificar las señales de muerte remotas. AFAIK No es posible obtener una "identificación" de una goroutina y matarla si se porta mal (es decir: atascado en un bucle infinito).
Aquí hay un ejemplo simple que probé:
package main
import (
"launchpad.net/tomb"
"time"
"fmt"
)
type Proc struct {
Tomb tomb.Tomb
}
func (proc *Proc) Exec() {
defer proc.Tomb.Done() // Must call only once
for {
select {
case <-proc.Tomb.Dying():
return
default:
time.Sleep(300 * time.Millisecond)
fmt.Println("Loop the loop")
}
}
}
func main() {
proc := &Proc{}
go proc.Exec()
time.Sleep(1 * time.Second)
proc.Tomb.Kill(fmt.Errorf("Death from above"))
err := proc.Tomb.Wait() // Will return the error that killed the proc
fmt.Println(err)
}
La salida debe verse como:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
Personalmente, me gustaría usar el rango en un canal en una goroutina:
https://play.golang.org/p/qt48vvdu8cd
Dave ha escrito una gran publicación sobre esto: http://dave.cheney.net/2013/04/30/curious-channels.