Wie man eine Goroutine stoppt
Frage
Ich habe eine Goroutine, die eine Methode aufruft, und übergibt den zurückgegebenen Wert auf einem Kanal:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Wie stoppe ich so eine Goroutine?
Lösung
BEARBEITEN: Ich habe diese Antwort in Eile geschrieben, bevor ich feststellte, dass es in Ihrer Frage darum geht, Werte an einen Chan in einer Goroutine zu senden. Der folgende Ansatz kann entweder mit einem zusätzlichen Chan verwendet werden, wie oben vorgeschlagen, oder mit der Tatsache, dass der Chan, den Sie bereits haben, bidirektional ist, können Sie nur den ... verwenden ...
Wenn Ihre Goroutine ausschließlich vorhanden ist, um die aus dem Chan stehenden Gegenstände zu verarbeiten, können Sie das "enge" gebaute und das spezielle Empfangsformular für Kanäle verwenden.
Das heißt, sobald Sie mit dem Versenden von Gegenständen auf dem Chan fertig sind, schließen Sie es. In Ihrer Goroutine erhalten Sie dann einen zusätzlichen Parameter für den Empfangsbetreiber, der zeigt, ob der Kanal geschlossen wurde.
Hier ist ein komplettes Beispiel (die Karteigruppe wird verwendet, um sicherzustellen, dass der Vorgang fortgesetzt wird, bis die Goroutine abgeschlossen ist):
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()
}
Andere Tipps
Normalerweise passieren Sie den Signalkanal von Goroutine A (möglicherweise separat). Dieser Signalkanal wird verwendet, um einen Wert in den Stopp der Goroutine zu drücken. Die Goroutine befragt, die regelmäßig kanalisieren. Sobald es ein Signal erkennt, beendet es.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Sie können keine Goroutine von außen töten. Sie können eine Goroutine signalisieren, dass sie die Verwendung eines Kanals nicht mehr verwenden, aber es gibt keinen Griff für Goroutines, um irgendeine Art von Meta -Management durchzuführen. Goroutinen sollen kooperativ Probleme lösen, sodass das Töten eines, das sich schlecht benimmt, fast nie eine angemessene Reaktion wäre. Wenn Sie Isolation für Robustheit wünschen, möchten Sie wahrscheinlich einen Prozess.
Im Allgemeinen können Sie einen Kanal erstellen und ein Stoppsignal in der Goroutine erhalten.
In diesem Beispiel zwei Möglichkeiten, Kanal zu erstellen.
Kanal
Kontext. In dem Beispiel werde ich Demo
context.WithCancel
Die erste Demo, verwenden Sie 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")
}
Die zweite Demo, verwenden Sie 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")
}
Ich weiß Grab Paket. Es ist im Grunde genommen ein Verkleinerungskanal, aber es tut auch schöne Dinge wie Fehler zurück. Die Routine unter Kontrolle hat immer noch die Verantwortung für die Überprüfung nach Fernkill -Signalen. AFAIK Es ist nicht möglich, eine "Ausweise" einer Goroutine zu bekommen und sie zu töten, wenn es schlecht sich verhalten (dh in einer unendlichen Schleife stecken).
Hier ist ein einfaches Beispiel, das ich getestet habe:
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)
}
Die Ausgabe sollte aussehen wie:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
Persönlich möchte ich Range auf einem Kanal in einer Goroutine verwenden:
https://play.golang.org/p/qt48vvdu8cd
Dave hat einen tollen Beitrag darüber geschrieben: http://dave.cheney.net/2013/04/30/curious-channels.