Comment arrêter un goroutine
Question
I ai un goroutine qui appelle une méthode, et transmet la valeur de retour sur un canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Comment puis-je arrêter un tel goroutine?
La solution
EDIT: J'ai écrit cette réponse en hâte, avant de se rendre compte que votre question porte sur l'envoi de valeurs à un chan dans un goroutine. L'approche ci-dessous peut être utilisé soit avec un chan supplémentaire comme suggéré ci-dessus, ou en utilisant le fait que le chan que vous avez déjà est bi-directionnel, vous pouvez utiliser juste une ...
Si votre goroutine existe pour traiter uniquement les éléments qui sortent du chan, vous pouvez utiliser la « proximité » et la spéciale builtin reçoivent le formulaire pour les canaux.
C'est, une fois que vous avez terminé l'envoi d'articles sur le chan, vous fermez. Ensuite, à l'intérieur de votre goroutine vous obtenez un paramètre supplémentaire à la réception opérateur qui indique si le canal a été fermé.
Voici un exemple complet (le waitgroup est utilisé pour assurer que le processus se poursuit jusqu'à ce que les finalise de 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()
}
Autres conseils
Typiquement, on passe la goroutine un canal de signal (éventuellement séparé). Ce canal de signal est utilisé pour pousser une valeur dans quand vous voulez que le goroutine pour arrêter. Les sondages de ce canal goroutine régulièrement. Dès qu'il détecte un signal, il se ferme.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Vous ne pouvez pas tuer un goroutine de l'extérieur. Vous pouvez signaler un goroutine à cesser d'utiliser un canal, mais il n'y a pas de poignée sur goroutines de faire toute sorte de méta gestion. Goroutines sont destinés à résoudre les problèmes en collaboration, afin de tuer celui qui se conduit mal serait presque jamais une réponse adéquate. Si vous voulez l'isolement pour la robustesse, vous voulez probablement un processus.
Generally, you could create a channel and receive a stop signal in the goroutine.
There two way to create channel in this example.
channel
context. In the example I will demo
context.WithCancel
The first demo, use 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")
}
The second demo, 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")
}
I know this answer has already been accepted, but I thought I'd throw my 2cents in. I like to use the tomb package. It's basically a suped up quit channel, but it does nice things like pass back any errors as well. The routine under control still has the responsibility of checking for remote kill signals. Afaik it's not possible to get an "id" of a goroutine and kill it if it's misbehaving (ie: stuck in an infinite loop).
Here's a simple example which I tested:
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)
}
The output should look like:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
Personally, I'd like to use range on a channel in a goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave has written a great post about this: http://dave.cheney.net/2013/04/30/curious-channels.