J'ai un goroutine qui appelle une méthode et transmet la valeur renvoyée sur un canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Comment puis-je arrêter un tel goroutine?
EDIT: J'ai écrit cette réponse à la hâte, avant de réaliser que votre question concernait l'envoi de valeurs à un chan à l'intérieur d'un goroutine. L'approche ci-dessous peut être utilisée avec un canal supplémentaire comme suggéré ci-dessus, ou en utilisant le fait que le canal que vous avez déjà est bidirectionnel, vous pouvez utiliser celui-ci uniquement ...
Si votre goroutine existe uniquement pour traiter les éléments qui sortent du canal, vous pouvez utiliser la commande "fermer" et le formulaire de réception spécial pour les chaînes.
C'est-à-dire qu'une fois que vous avez fini d'envoyer des éléments sur le canal, vous le fermez. Ensuite, dans votre goroutine, vous recevez un paramètre supplémentaire pour l'opérateur de réception indiquant si le canal a été fermé.
Voici un exemple complet (le groupe d’attente sert à s’assurer que le processus se poursuit jusqu’à ce que la goroutine soit terminée):
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()
}
En général, vous transmettez à la goroutine un canal de signal (éventuellement séparé). Ce canal de signal est utilisé pour insérer une valeur lorsque vous voulez que le goroutine s’arrête. Le goroutine interroge cette chaîne 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 indiquer à un goroutine de ne plus utiliser de canal, mais les goroutines ne peuvent gérer aucune méta-gestion. Les Goroutines sont destinées à résoudre les problèmes de manière coopérative, donc en tuer un qui se comporte mal ne constituerait presque jamais une réponse adéquate. Si vous souhaitez que l'isolation soit robuste, vous souhaitez probablement un processus.
Généralement, vous pouvez créer un canal et recevoir un signal d'arrêt dans le goroutine.
Il y a deux façons de créer un canal dans cet exemple.
canal
contexte. Dans l'exemple, je vais démo context.WithCancel
La première démo, utilisez 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 deuxième démo, utilisez 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")
}
Je sais que cette réponse a déjà été acceptée, mais je pensais jeter mes 2cents. J'aime utiliser le package tomb . Il s’agit en fait d’un canal arrêté, mais il fait des choses agréables, comme le renvoi d’erreurs. La routine sous contrôle a toujours la responsabilité de vérifier les signaux d'abattage à distance. Après tout, il n'est pas possible d'obtenir un "identifiant" de goroutine et de le tuer s'il se comporte mal (c'est-à-dire coincé dans une boucle infinie).
Voici un exemple simple que j'ai testé:
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 sortie devrait ressembler à:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
Personnellement, j'aimerais utiliser la gamme sur une chaîne dans un goroutine:
https://play.golang.org/p/qt48vvDu8cd
Dave a écrit un excellent article à ce sujet: http://dave.cheney.net/2013/04/30/curious-channels .