3 votes

Une question sur la routine principale et la routine de l'enfant qui sont écoutées simultanément sur la même chaîne.

func main() {
    c := make(chan os.Signal, 1)
    signal.Notify(c)

    ticker := time.NewTicker(time.Second)
    stop := make(chan bool)

    go func() {
        defer func() { stop <- true }()
        for {
            select {
            case <-ticker.C:
                fmt.Println("Tick")
            case <-stop:
                fmt.Println("Goroutine closing")
                return
            }
        }
    }()

    <-c
    ticker.Stop()

    stop <- true

    <-stop
    fmt.Println("Application stopped")
}

Peu importe le nombre de fois où j'ai exécuté le code ci-dessus, j'ai obtenu le même résultat. C'est-à-dire que "Goroutine closing" est toujours imprimé avant "Application stopped" après que j'ai appuyé sur Ctrl + C .

Je pense que, théoriquement, il y a une chance que "Goroutine closing" ne soit pas imprimé du tout. Ai-je raison ? Malheureusement, je n'obtiens jamais ce résultat théorique.

BTW : Je sais que la lecture et l'écriture d'un canal en une seule routine doivent être évitées. Ignorez cela temporairement.

3voto

Leon Points 2521

Dans votre cas, Goroutine closing sera toujours exécuté et il sera toujours imprimé avant Application stopped parce que votre stop n'est pas mis en mémoire tampon. Cela signifie que l'envoi sera bloqué jusqu'à ce que le résultat soit reçu.

Dans votre code, le stop <- true dans votre main bloquera jusqu'à ce que la goroutine ait reçu la valeur, ce qui fera que le canal sera à nouveau vide. Ensuite, la <-stop dans votre main bloquera jusqu'à ce qu'une autre valeur soit envoyée au canal, ce qui se produit lorsque votre goroutine revient après avoir imprimé Goroutine closing .

Si vous initialisiez votre canal d'une manière tamponnée

stop := make(chan bool, 1)

entonces Goroutine closing pourrait ne pas être exécuté. Pour voir cela, vous pouvez ajouter un time.Sleep juste après l'impression Tick car cela rend ce cas plus probable (il se produira chaque fois que vous appuyez sur Ctrl + C pendant le sommeil).

Utilisation d'un sync.WaitGroup pour attendre la fin des goroutines est une bonne alternative, surtout si vous devez attendre plus d'une goroutine. Vous pouvez également utiliser un context.Context pour arrêter les goroutines. Retravailler votre code pour utiliser ces deux méthodes pourrait ressembler à ceci :

func main() {
    c := make(chan os.Signal, 1)
    signal.Notify(c)

    ticker := time.NewTicker(time.Second)
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup

    wg.Add(1)
    go func() {
        defer func() { wg.Done() }()
        for {
            select {
            case <-ctx.Done():
                fmt.Println("Goroutine closing")
                return
            case <-ticker.C:
                fmt.Println("Tick")
                time.Sleep(time.Second)
            }
        }
    }()

    <-c
    ticker.Stop()

    cancel()

    wg.Wait()

    fmt.Println("Application stopped")
}

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