Tengo una goroutine 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 tal gorutina?
Tengo una goroutine 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 tal gorutina?
Respuestas:
EDITAR: Escribí esta respuesta apresuradamente, antes de darme cuenta de que su pregunta es sobre enviar valores a un chan dentro de una goroutine. El siguiente enfoque se puede usar con un chan adicional como se sugirió anteriormente, o usando el hecho de que el chan que ya tiene es bidireccional, puede usar solo el uno ...
Si su goroutine existe únicamente para procesar los elementos que salen del canal, puede hacer uso del "cerrar" incorporado y del formulario de recepción especial para canales.
Es decir, una vez que hayas terminado de enviar elementos al chan, lo cierras. Luego, dentro de su goroutine, obtiene un parámetro adicional para el operador de recepción que muestra si el canal se ha 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 goroutine):
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()
}
defer
to call wg.Done()
y un range ch
bucle para iterar sobre todos los valores hasta que se cierra el canal.
Normalmente, pasa la goroutine por un canal de señal (posiblemente separado). Ese canal de señal se utiliza para introducir un valor cuando desee que se detenga la gorutina. Las encuestas goroutine que canalizan habitualmente. 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
No se puede matar una gorutina desde fuera. Puede indicarle a una goroutine que deje de usar un canal, pero no hay control de goroutines para hacer ningún tipo de metagestión. Las gorutinas están destinadas a resolver problemas de manera cooperativa, por lo que matar a uno que se está portando mal casi nunca sería una respuesta adecuada. Si desea aislamiento por robustez, probablemente desee un proceso.
Generalmente, puede crear un canal y recibir una señal de parada en la goroutine.
Hay dos formas de crear un canal en este ejemplo.
canal
contexto . En el ejemplo haré una demostracióncontext.WithCancel
La primera demostración, usa 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")
}
Sé que esta respuesta ya ha sido aceptada, pero pensé en tirar mis 2 centavos. Me gusta usar el paquete de la tumba . Es básicamente un canal para dejar de fumar mejorado, pero también hace cosas buenas como devolver cualquier error. La rutina bajo control todavía tiene la responsabilidad de verificar las señales de apagado remoto. Afaik no es posible obtener un "id" de una goroutine y matarla si se está portando mal (es decir, atascada 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
tomb
hace con la goroutine en caso de que ocurra algo dentro de ella que provoque pánico, por ejemplo? Técnicamente hablando, la goroutine sale en este caso, así que supongo que todavía llamará al diferido proc.Tomb.Done()
...
proc.Tomb.Done()
lo ejecutaría antes de que el pánico colapse el programa, pero ¿con qué fin? Es posible que la goroutine principal tenga una ventana de oportunidad muy pequeña para ejecutar algunas sentencias, pero no tiene forma de recuperarse de un pánico en otra goroutine, por lo que el programa aún falla. Los doc momento en el que el programa se bloquea ".
Personalmente, me gustaría usar rango en un canal en una goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave ha escrito una gran publicación sobre esto: http://dave.cheney.net/2013/04/30/curious-channels .