Question

I ai un goroutine qui appelle une méthode, et transmet la valeur de retour sur un canal:

ch := make(chan int, 100)
go func(){
    for {
        ch <- do_stuff()
    }
}()

Comment puis-je arrêter un tel goroutine?

Était-ce utile?

La solution

EDIT: J'ai écrit cette réponse en hâte, avant de se rendre compte que votre question porte sur l'envoi de valeurs à un chan dans un goroutine. L'approche ci-dessous peut être utilisé soit avec un chan supplémentaire comme suggéré ci-dessus, ou en utilisant le fait que le chan que vous avez déjà est bi-directionnel, vous pouvez utiliser juste une ...

Si votre goroutine existe pour traiter uniquement les éléments qui sortent du chan, vous pouvez utiliser la « proximité » et la spéciale builtin reçoivent le formulaire pour les canaux.

C'est, une fois que vous avez terminé l'envoi d'articles sur le chan, vous fermez. Ensuite, à l'intérieur de votre goroutine vous obtenez un paramètre supplémentaire à la réception opérateur qui indique si le canal a été fermé.

Voici un exemple complet (le waitgroup est utilisé pour assurer que le processus se poursuit jusqu'à ce que les finalise de goroutine):

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()
}

Autres conseils

Typiquement, on passe la goroutine un canal de signal (éventuellement séparé). Ce canal de signal est utilisé pour pousser une valeur dans quand vous voulez que le goroutine pour arrêter. Les sondages de ce canal goroutine 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 à cesser d'utiliser un canal, mais il n'y a pas de poignée sur goroutines de faire toute sorte de méta gestion. Goroutines sont destinés à résoudre les problèmes en collaboration, afin de tuer celui qui se conduit mal serait presque jamais une réponse adéquate. Si vous voulez l'isolement pour la robustesse, vous voulez probablement un processus.

Generally, you could create a channel and receive a stop signal in the goroutine.

There two way to create channel in this example.

  1. channel

  2. context. In the example I will demo context.WithCancel

The first demo, use 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")
}

The second demo, use 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")
}

I know this answer has already been accepted, but I thought I'd throw my 2cents in. I like to use the tomb package. It's basically a suped up quit channel, but it does nice things like pass back any errors as well. The routine under control still has the responsibility of checking for remote kill signals. Afaik it's not possible to get an "id" of a goroutine and kill it if it's misbehaving (ie: stuck in an infinite loop).

Here's a simple example which I tested:

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)
}

The output should look like:

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

Personally, I'd like to use range on a channel in a goroutine:

https://play.golang.org/p/qt48vvDu8cd

Dave has written a great post about this: http://dave.cheney.net/2013/04/30/curious-channels.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top