Come fermare un goroutine
Domanda
Ho un goroutine che chiama un metodo, e passa valore restituito su un canale:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Come si interrompe una simile goroutine?
Soluzione
Modifica Ho scritto questa risposta in gran fretta, prima di rendersi conto che la tua domanda è circa l'invio di valori ad un chan all'interno di un goroutine. L'approccio seguito può essere utilizzato sia con un ulteriore chan come suggerito sopra, o sfruttando il fatto che la Chan si dispone già è bidirezionale, è possibile utilizzare solo quello ...
Se il goroutine esiste solamente per elaborare gli elementi in uscita dal chan, si può fare uso del "chiudere" incorporato e la speciale ricevono modulo per i canali.
Cioè, una volta che hai finito di inviare articoli sul chan, si chiude. Poi all'interno del vostro goroutine si ottiene un parametro in più per l'operatore che indica se il canale è stato chiuso ricezione.
Ecco un esempio completo (il waitgroup è usato per fare in modo che il processo continua fino al completamento 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()
}
Altri suggerimenti
In genere, si passa il goroutine un canale di segnale (forse a parte). Questo canale di segnale viene utilizzato per spingere un valore in cui si desidera che il goroutine di fermarsi. I sondaggi goroutine quel canale regolarmente. 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 si può uccidere un goroutine dall'esterno. È possibile segnalare un goroutine di smettere di usare un canale, ma non c'è maniglia goroutines di fare qualsiasi tipo di meta-management. Goroutines sono destinati a risolvere i problemi in modo cooperativo, in modo da uccidere uno che è comportamento anomalo sarebbe quasi mai essere una risposta adeguata. Se si desidera che l'isolamento per la robustezza, probabilmente avrete bisogno di un processo.
In generale, si potrebbe creare un canale e di ricevere un segnale di arresto nella goroutine.
Ci due modi per creare il canale in questo esempio.
-
canale
-
contesto . Nell'esempio io demo
context.WithCancel
Il primo demo, uso 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")
}
Il secondo demo, uso 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 i miei 2Cents. Mi piace usare la pacchetto tomba . E 'fondamentalmente un suped up smettere di canale, ma fa le cose belle come passare di nuovo eventuali errori pure. La routine sotto controllo ha ancora la responsabilità di verificare per i segnali di uccidere a distanza. Per quanto ne so non è possibile ottenere un "id" di un goroutine e ucciderlo se è comportarsi male. (Es: bloccato in un ciclo infinito)
Ecco un semplice esempio che ho provato:
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:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
Personalmente, mi piacerebbe usare gamma su un canale in un goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave ha scritto un bel post su questo: http: // dave. cheney.net/2013/04/30/curious-channels .