J'ai un goroutine qui appelle une méthode et passe la valeur retournée sur un canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Comment arrêter une telle goroutine?
J'ai un goroutine qui appelle une méthode et passe la valeur retournée sur un canal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Comment arrêter une telle goroutine?
Réponses:
EDIT: J'ai écrit cette réponse à la hâte, avant de réaliser que votre question porte sur l'envoi de valeurs à un chan dans un goroutine. L'approche ci-dessous peut être utilisée soit avec un chan supplémentaire comme suggéré ci-dessus, soit en utilisant le fait que le chan que vous avez déjà est bidirectionnel, vous pouvez utiliser juste celui ...
Si votre goroutine existe uniquement pour traiter les éléments sortant du chan, vous pouvez utiliser la fonction intégrée "close" et le formulaire de réception spécial pour les chaînes.
Autrement dit, une fois que vous avez terminé d'envoyer des éléments sur le chan, vous le fermez. Ensuite, à l'intérieur de votre goroutine, vous obtenez un paramètre supplémentaire pour l'opérateur de réception qui indique si le canal a été fermé.
Voici un exemple complet (le waitgroup est utilisé pour s'assurer que le processus continue jusqu'à ce que la goroutine se termine):
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()
, et une range ch
boucle pour itérer sur toutes les valeurs jusqu'à ce que le canal soit fermé.
En règle générale, vous passez la goroutine par un canal de signal (éventuellement séparé). Ce canal de signal est utilisé pour insérer une valeur lorsque vous souhaitez que le goroutine s'arrête. Les sondages goroutine qui canalisent 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 d'arrêter d'utiliser un canal, mais il n'y a pas de poignée sur les goroutines pour faire une sorte de méta-gestion. Les goroutines sont destinées à résoudre les problèmes de manière coopérative, donc tuer celui qui se comporte mal ne serait presque jamais une réponse adéquate. Si vous voulez une isolation pour la robustesse, vous voulez probablement un processus.
Généralement, vous pouvez créer un canal et recevoir un signal d'arrêt dans le goroutine.
Il existe deux façons de créer un canal dans cet exemple.
canal
contexte . Dans l'exemple, je vais faire une démonstrationcontext.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 j'ai pensé jeter mes 2cents dedans. J'aime utiliser le paquet tomb . C'est fondamentalement un canal d'arrêt remplacé, mais il fait également de bonnes choses comme renvoyer les erreurs. La routine sous contrôle a toujours la responsabilité de vérifier les signaux d'arrêt à distance. Afaik il n'est pas possible d'obtenir un "identifiant" d'un goroutine et de le tuer s'il se comporte mal (ie: 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
tomb
fait la goroutine au cas où quelque chose se passerait à l'intérieur et semerait la panique, par exemple? Techniquement parlant, le goroutine sort dans ce cas, donc je suppose qu'il appellera toujours le différé proc.Tomb.Done()
...
proc.Tomb.Done()
serait exécuté avant que la panique plante le programme, mais à quelle fin? Il est possible que le goroutine principal ait une très petite fenêtre d'opportunité pour exécuter certaines instructions, mais il n'a aucun moyen de se remettre d'une panique dans un autre goroutine, donc le programme plante toujours. Les documents disent: "Lorsque la fonction F appelle panique, l'exécution de F s'arrête, toutes les fonctions différées de F sont exécutées normalement, puis F retourne à son appelant .. Le processus continue dans la pile jusqu'à ce que toutes les fonctions de la goroutine actuelle soient retournées, à quel moment le programme se bloque. "
Personnellement, j'aimerais utiliser range sur un canal 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 .