Ho una goroutine che chiama un metodo e passa il valore restituito su un canale:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Come faccio a fermare una simile goroutine?
Ho una goroutine che chiama un metodo e passa il valore restituito su un canale:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Come faccio a fermare una simile goroutine?
Risposte:
EDIT: Ho scritto questa risposta in fretta, prima di rendermi conto che la tua domanda riguarda l'invio di valori a un canale all'interno di una goroutine. L'approccio di seguito può essere utilizzato con un canale aggiuntivo come suggerito sopra, o utilizzando il fatto che il canale che hai già è bidirezionale, puoi usare solo quello ...
Se la tua goroutine esiste esclusivamente per elaborare gli elementi che escono dal canale, puoi utilizzare il builtin "chiudi" e il modulo di ricezione speciale per i canali.
Cioè, una volta che hai finito di inviare elementi sul canale, lo chiudi. Quindi all'interno della tua goroutine ottieni un parametro extra per l'operatore di ricezione che mostra se il canale è stato chiuso.
Ecco un esempio completo (il gruppo di attesa viene utilizzato per assicurarsi che il processo continui fino al completamento della 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()
e un range ch
ciclo per iterare su tutti i valori fino a quando il canale non viene chiuso.
In genere, si passa alla goroutine un canale di segnale (possibilmente separato). Quel canale del segnale viene utilizzato per inserire un valore quando si desidera che la goroutine si fermi. La goroutine interroga regolarmente quel canale. Non appena rileva un segnale, si chiude.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Non puoi uccidere una goroutine dall'esterno. Puoi segnalare a una goroutine di smettere di usare un canale, ma sulle goroutine non c'è alcuna possibilità di eseguire alcun tipo di meta-gestione. I Goroutine hanno lo scopo di risolvere i problemi in modo cooperativo, quindi ucciderne uno che si comporta male non sarebbe quasi mai una risposta adeguata. Se vuoi l'isolamento per la robustezza, probabilmente vuoi un processo.
In generale, potresti creare un canale e ricevere un segnale di stop nella goroutine.
Ci sono due modi per creare il canale in questo esempio.
canale
contesto . Nell'esempio farò una democontext.WithCancel
La prima demo, 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 seconda demo, usa 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")
}
So che questa risposta è già stata accettata, ma ho pensato di buttare dentro i miei 2 centesimi. Mi piace usare il pacchetto tomb . È fondamentalmente un canale di chiusura potenziato, ma fa anche cose carine come restituire eventuali errori. La routine sotto controllo ha ancora la responsabilità di verificare i segnali di kill remoto. Non è possibile ottenere un "id" di una goroutine e ucciderla se si comporta in modo anomalo (cioè: bloccato in un ciclo infinito).
Ecco un semplice esempio che ho testato:
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)
}
L'output dovrebbe essere simile a:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
fa con la goroutine nel caso in cui succeda qualcosa al suo interno che getta il panico, per esempio? Tecnicamente parlando, la goroutine esce in questo caso, quindi presumo che chiamerà ancora il differito proc.Tomb.Done()
...
proc.Tomb.Done()
verrebbe eseguito prima che il panico blocchi il programma, ma a che scopo? È possibile che la goroutine principale abbia una finestra di opportunità molto piccola per eseguire alcune istruzioni, ma non ha modo di riprendersi da un panico in un'altra goroutine, quindi il programma continua a bloccarsi. I documenti dicono: "Quando la funzione F chiama panico, l'esecuzione di F si interrompe, tutte le funzioni differite in F vengono eseguite normalmente, e quindi F ritorna al suo chiamante .. Il processo continua sullo stack finché tutte le funzioni nella goroutine corrente non sono tornate, a quel punto il programma va in crash. "
Personalmente, vorrei utilizzare l'intervallo su un canale in una goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave ha scritto un ottimo post su questo: http://dave.cheney.net/2013/04/30/curious-channels .