Ich habe eine Goroutine, die eine Methode aufruft und den zurückgegebenen Wert auf einem Kanal übergibt:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Wie stoppe ich eine solche Goroutine?
Ich habe eine Goroutine, die eine Methode aufruft und den zurückgegebenen Wert auf einem Kanal übergibt:
ch := make(chan int, 100)
go func(){
for {
ch <- do_stuff()
}
}()
Wie stoppe ich eine solche Goroutine?
Antworten:
EDIT: Ich habe diese Antwort in Eile geschrieben, bevor mir klar wurde, dass es bei Ihrer Frage darum geht, Werte an einen Chan innerhalb einer Goroutine zu senden. Der folgende Ansatz kann entweder mit einem zusätzlichen Chan verwendet werden, wie oben vorgeschlagen, oder unter Verwendung der Tatsache, dass der Chan, den Sie bereits haben, bidirektional ist, können Sie nur den einen ...
Wenn Ihre Goroutine nur zur Verarbeitung der aus dem Chan kommenden Elemente vorhanden ist, können Sie das integrierte "Schließen" und das spezielle Empfangsformular für Kanäle verwenden.
Das heißt, sobald Sie mit dem Senden von Elementen auf dem Chan fertig sind, schließen Sie ihn. Dann erhalten Sie in Ihrer Goroutine einen zusätzlichen Parameter für den Empfangsoperator, der anzeigt, ob der Kanal geschlossen wurde.
Hier ist ein vollständiges Beispiel (die Wartegruppe wird verwendet, um sicherzustellen, dass der Prozess 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()
}
defer
aufgerufen wird wg.Done()
, und eine range ch
Schleife, um alle Werte zu durchlaufen, bis der Kanal geschlossen wird.
In der Regel übergeben Sie der Goroutine einen (möglicherweise separaten) Signalkanal. Dieser Signalkanal wird verwendet, um einen Wert zu drücken, wenn die Goroutine gestoppt werden soll. Die Goroutine fragt diesen Kanal regelmäßig ab. Sobald es ein Signal erkennt, wird es beendet.
quit := make(chan bool)
go func() {
for {
select {
case <- quit:
return
default:
// Do other stuff
}
}
}()
// Do stuff
// Quit goroutine
quit <- true
Sie können eine Goroutine nicht von außen töten. Sie können einer Goroutine signalisieren, dass sie keinen Kanal mehr verwenden soll, aber Goroutinen haben keine Möglichkeit, Meta-Management zu betreiben. Goroutinen sollen Probleme kooperativ lösen, daher wäre es fast nie angemessen, jemanden zu töten, der sich schlecht benimmt. 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 empfangen.
In diesem Beispiel gibt es zwei Möglichkeiten, einen Kanal zu erstellen.
Kanal
Kontext . Im Beispiel werde ich Democontext.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 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ß , diese Antwort wird bereits angenommen, aber ich dachte , dass ich meine 2cents in werfen würde. Ich mag das verwenden , Grab - Paket. Es ist im Grunde ein beschleunigter Beendigungskanal, aber es macht auch nette Dinge wie das Zurückgeben von Fehlern. Die kontrollierte Routine hat weiterhin die Verantwortung, nach Remote-Kill-Signalen zu suchen. Afaik ist es nicht möglich, eine "ID" einer Goroutine zu erhalten und sie zu töten, wenn sie sich schlecht benimmt (dh in einer Endlosschleife steckt).
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 folgendermaßen aussehen:
# Loop the loop
# Loop the loop
# Loop the loop
# Loop the loop
# Death from above
tomb
mit der Goroutine passiert, falls etwas in ihr passiert, das zum Beispiel Panik auslöst? Technisch gesehen wird die Goroutine in diesem Fall beendet, daher gehe ich davon aus, dass die verzögerte proc.Tomb.Done()
...
proc.Tomb.Done()
würde ja ausführen, bevor die Panik das Programm zum Absturz bringt, aber zu welchem Zweck? Es ist möglich, dass die Hauptgoroutine ein sehr kleines Zeitfenster hat, um einige Anweisungen auszuführen, aber es gibt keine Möglichkeit, sich von einer Panik in einer anderen Goroutine zu erholen, sodass das Programm immer noch abstürzt. Die Ärzte sagen: "Wenn die Funktion F Panik auslöst, wird die Ausführung von F gestoppt, alle zurückgestellten Funktionen in F werden normal ausgeführt, und dann kehrt F zu seinem Aufrufer zurück. Der Prozess setzt den Stapel fort, bis alle Funktionen in der aktuellen Goroutine zurückgekehrt sind. An diesem Punkt stürzt das Programm ab. "
Persönlich möchte ich die Reichweite eines Kanals in einer Goroutine verwenden:
https://play.golang.org/p/qt48vvDu8cd
Dave hat einen großartigen Beitrag dazu geschrieben: http://dave.cheney.net/2013/04/30/curious-channels .