go - attribute - title label html
cómo detener un goroutine (6)
Tengo una rutina 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 un goroutine?
En general, puede crear un canal y recibir una señal de parada en la rutina de ir.
Hay dos formas de crear canales en este ejemplo.
canal
contexto . En el ejemplo voy a demo
context.WithCancel
La primera demostración, use el 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, use 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")
}
No puedes matar a un goroutine desde afuera. Puede indicarle a una rutina que deje de usar un canal, pero no se manejan las rutinas para hacer ningún tipo de metagestión. Los goroutines están destinados a resolver los problemas de forma cooperativa, por lo que matar a uno que se está portando mal casi nunca será una respuesta adecuada. Si desea aislamiento para mayor robustez, probablemente desee un proceso.
Personalmente, me gustaría usar el rango en un canal en un goroutine:
http://play.golang.org/p/KjG8FLzPoz
Dave ha escrito una excelente publicación sobre esto: http://dave.cheney.net/2013/04/30/curious-channels .
Por lo general, pasas el canal de señal de la goroutine (posiblemente por separado). Ese canal de señal se usa para insertar un valor cuando quieres que la rutina se detenga. Las encuestas de goroutine que canalizan regularmente. Tan pronto como detecta una señal, se cierra.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Sé que esta respuesta ya ha sido aceptada, pero pensé que lanzaría mis 2cents. Me gusta usar el paquete de la tomb . Básicamente es un canal de escape suprimido, pero hace cosas buenas, como pasar los errores también. La rutina bajo control todavía tiene la responsabilidad de verificar las señales de muerte remota. Afaik no es posible obtener una "identificación" de un goroutine y matarlo si se está portando mal (es decir: atrapado 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 debería verse así:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
EDITAR: Escribí esta respuesta a toda prisa, antes de darme cuenta de que su pregunta es sobre el envío de valores a un chan dentro de un goroutine. El siguiente enfoque se puede utilizar con un chan adicional como se sugiere arriba, o usando el hecho de que el chan que ya tienes es bidireccional, puedes usar solo el que ...
Si tu goroutine existe únicamente para procesar los elementos que salen del chan, puedes utilizar el builtin "cerrado" y el formulario de recepción especial para canales.
Es decir, una vez que hayas terminado de enviar elementos al chan, lo cierras. Luego dentro de tu goroutine obtienes un parámetro adicional para el 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 la rutina termine):
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()
}