J'ai une 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 une telle goroutine ?
J'ai une 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 une telle goroutine ?
En général, vous passez à la goroutine un canal de signal (éventuellement séparé). Ce canal de signal est utilisé pour y insérer une valeur lorsque vous voulez que la goroutine s'arrête. La goroutine vérifie régulièrement ce canal. Dès qu'elle détecte un signal, elle s'arrête.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Faire autre chose
}
}
}()
// Faire des choses
// Arrêter la goroutine
quit <- true
Elazar, Ce que vous suggérez est une façon d'arrêter une fonction après l'avoir appelée. Une goroutine n'est pas un thread. Elle peut s'exécuter dans un thread différent ou dans le même thread que le vôtre. Je ne connais aucun langage qui supporte ce que vous semblez penser que Go devrait supporter.
EDIT: J'ai écrit cette réponse à la hâte, avant de réaliser que votre question concerne l'envoi de valeurs à un chan à l'intérieur d'une 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-ci...
Si votre goroutine existe uniquement pour traiter les éléments qui sortent du chan, vous pouvez utiliser la fonction "close" intégrée et la forme spéciale de réception pour les canaux.
C'est-à-dire, une fois que vous avez fini 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 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()
}
Vous ne pouvez pas tuer un goroutine depuis l'extérieur. Vous pouvez signaler à un goroutine d'arrêter en utilisant un canal, mais il n'y a aucun moyen de contrôler les goroutines pour effectuer une sorte de gestion méta. Les goroutines sont conçues pour résoudre des problèmes de manière coopérative, donc tuer une goroutine qui se comporte mal ne serait presque jamais une réponse adéquate. Si vous voulez l'isolement pour la robustesse, vous voulez probablement un processus.
Et vous voudrez peut-être vous pencher sur le package d'encodage/gob, qui permettrait à deux programmes Go d'échanger facilement des structures de données via un canal.
Dans mon cas, j'ai une goroutine qui sera bloquée sur un appel système, et j'ai besoin de lui dire d'interrompre l'appel système puis de sortir. Si j'étais bloqué sur une lecture de canal, il serait possible de faire comme vous le suggérez.
J'ai vu ce problème auparavant. La façon dont nous l'avons "résolu" était d'augmenter le nombre de threads au démarrage de l'application pour correspondre au nombre de goroutines possibles + le nombre de processeurs.
Je sais que cette réponse a déjà été acceptée, mais je pense que je vais donner mon avis. J'aime utiliser le package tomb. C'est essentiellement un canal de sortie amélioré, mais il fait des choses sympas comme renvoyer les erreurs éventuelles également. La routine sous contrôle a toujours la responsabilité de vérifier les signaux de fin à distance. À ma connaissance, il n'est pas possible d'obtenir un "id" d'une goroutine et de la tuer si elle se comporte mal (c'est-à-dire, bloquée 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() // Ne doit être appelé qu'une seule fois
for {
select {
case <-proc.Tomb.Dying():
return
default:
time.Sleep(300 * time.Millisecond)
fmt.Println("Boucle infinie")
}
}
}
func main() {
proc := &Proc{}
go proc.Exec()
time.Sleep(1 * time.Second)
proc.Tomb.Kill(fmt.Errorf("La mort venue d'en haut"))
err := proc.Tomb.Wait() // Renvoie l'erreur qui a tué le processus
fmt.Println(err)
}
La sortie devrait ressembler à :
# Boucle infinie
# Boucle infinie
# Boucle infinie
# Boucle infinie
# La mort venue d'en haut
Ce package est assez intéressant! Avez-vous essayé de voir ce que tomb
fait avec la goroutine en cas de problème à l'intérieur qui déclenche une panique, par exemple? Techniquement parlant, la goroutine se termine dans ce cas, donc je suppose qu'elle appellera toujours le reporté proc.Tomb.Done()
...
Salut Gwyneth, oui proc.Tomb.Done()
s'exécuterait avant que la panique ne fasse planter le programme, mais dans quel but? Il est possible que la goroutine principale ait une très courte fenêtre d'opportunité pour exécuter quelques instructions, mais elle n'a aucun moyen de récupérer d'une panique dans une autre goroutine, donc le programme plante toujours. La documentation dit: "Lorsque la fonction F appelle panic, l'exécution de F s'arrête, toutes les fonctions différées dans F sont exécutées normalement, puis F retourne à son appelant... Le processus continue à remonter la pile jusqu'à ce que toutes les fonctions dans la goroutine actuelle aient retourné, moment auquel le programme plante."
C'est une très bonne réponse - meilleure que d'utiliser le contexte dans certains cas. L'API est un peu différente mais l'utilisation est similaire ici avec tomb.v2.
Personnellement, j'aimerais utiliser une plage sur un canal dans une goroutine :
https://play.golang.org/p/qt48vvDu8cd
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
c := make(chan bool)
wg.Add(1)
go func() {
defer wg.Done()
for b := range c {
fmt.Printf("Bonjour %t\n", b)
}
}()
c <- true
c <- true
close(c)
wg.Wait()
}
Dave a écrit un excellent article à ce sujet : http://dave.cheney.net/2013/04/30/curious-channels.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
2 votes
Une autre réponse, en fonction de votre situation, est d'utiliser un contexte Go. Je n'ai ni le temps ni les connaissances pour créer une réponse à ce sujet. Je voulais juste le mentionner ici afin que les personnes qui recherchent et trouvent cette réponse insatisfaisante aient un autre fil à tirer (c'est un jeu de mots). Dans la plupart des cas, vous devriez faire comme le suggère la réponse acceptée. Cette réponse mentionne les contextes : stackoverflow.com/a/47302930/167958