tag attribute go goroutine channels

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.

  1. canal

  2. 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.



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() }