121 votes

Comment arrêter une goroutine

J'ai une 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 ?

1 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 nécessaires pour créer une réponse à ce sujet. Je voulais juste le mentionner ici pour que les personnes qui cherchent et trouvent cette réponse insatisfaisante aient un autre fil à tirer (jeu de mots). Dans la plupart des cas, vous devriez faire ce que la réponse acceptée suggère. Cette réponse mentionne les contextes : stackoverflow.com/a/47302930/167958

132voto

jimt Points 7028

Typiquement, vous passez à la goroutine un canal de signal (éventuellement séparé). Ce canal de signal est utilisé pour pousser une valeur quand vous voulez que la goroutine s'arrête. La goroutine interroge 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:
            // Do other stuff
        }
    }
}()

// Do stuff

// Quit goroutine
quit <- true

29 votes

Ce n'est pas suffisant. Et si la goroutine est coincée dans une boucle sans fin, à cause d'un bug ?

270 votes

Alors le bug devrait être corrigé.

13 votes

Elazar, ce que vous suggérez est un moyen 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 autre thread 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.

63voto

laslowh Points 3209

EDIT : J'ai écrit cette réponse à la hâte, avant de réaliser que votre question concerne l'envoi de valeurs à un chan dans 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 bi-directionnel, vous pouvez utiliser juste un...

Si votre goroutine existe uniquement pour traiter les éléments qui sortent du chan, vous pouvez utiliser le buildin "close" et le formulaire de réception spécial pour les canaux.

C'est-à-dire qu'une fois que vous avez fini d'envoyer des articles sur le chan, vous le fermez. Ensuite, dans votre goroutine, vous obtenez un paramètre supplémentaire à l'opérateur de réception qui indique si le canal a été fermé.

Voici un exemple complet (le groupe d'attente 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()
}

20 votes

Le corps de la goroutine interne est écrit de manière plus idiomatique en utilisant les éléments suivants defer d'appeler wg.Done() et un range ch boucle pour itérer sur toutes les valeurs jusqu'à ce que le canal soit fermé.

38voto

SteveMcQwark Points 724

On ne peut pas tuer une goroutine de l'extérieur. Vous pouvez signaler à une goroutine d'arrêter d'utiliser un canal, mais il n'y a pas de gestion des goroutines pour faire une sorte de méta-gestion. Les goroutines sont destinées à résoudre des problèmes de manière coopérative, donc en tuer une 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.

0 votes

Vous pouvez également vous intéresser au paquetage encoding/gob, qui permet à deux programmes Go d'échanger facilement des structures de données via un pipe.

1 votes

Dans mon cas, j'ai une goroutine qui sera bloquée sur un appel système, et j'ai besoin de lui dire d'abandonner l'appel système et de sortir. Si j'étais bloqué sur une lecture de canal, il serait possible de faire ce que vous suggérez.

0 votes

J'ai déjà vu ce problème. La façon dont nous l'avons "résolu" était d'augmenter le nombre de threads au début de l'application pour qu'il corresponde au nombre de goroutines possibles + le nombre de CPU.

14voto

Kevin Cantwell Points 211

Je sais que cette réponse a déjà été acceptée, mais j'ai pensé que je devais ajouter mon grain de sel. J'aime utiliser le tombeau paquet. C'est en fait un canal de sortie amélioré, mais il fait aussi des choses sympas comme renvoyer les erreurs. La routine sous contrôle a toujours la responsabilité de vérifier les signaux d'arrêt à distance. A ce jour, il n'est pas possible d'obtenir un "id" d'une goroutine et de la tuer si elle se comporte mal (c'est-à-dire si elle est coincé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() // 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 à ça :

# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above

0 votes

Ce paquet est très intéressant ! Avez-vous testé pour voir ce que tomb fait avec la goroutine au cas où quelque chose se produirait à l'intérieur de celle-ci et déclencherait une panique, par exemple ? D'un point de vue technique, la goroutine s'arrête dans ce cas, donc je suppose qu'elle appelle toujours la fonction différée proc.Tomb.Done() ...

1 votes

Bonjour 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 puisse avoir une très petit Une fenêtre d'opportunité pour exécuter certaines instructions, mais il n'y a aucun moyen de récupérer d'une panique dans une autre goroutine, donc le programme se plante quand même. Les docs disent : "Lorsque la fonction F appelle panic, 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 se poursuit sur la pile jusqu'à ce que toutes les fonctions de la goroutine actuelle soient retournées, auquel cas le programme s'arrête."

0 votes

C'est une excellente réponse - meilleure que l'utilisation du contexte dans certains cas . L'API est un peu différente, mais l'utilisation est similaire ici. tombeau.v2 .

8voto

mikespook Points 385

Personnellement, j'aimerais utiliser la gamme 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("Hello %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 .

1 votes

C'est vraiment magnifique.

Prograide.com

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.

Powered by:

X