Eine andere Antwort, abhängig von Ihrer Situation, ist die Verwendung eines Go-Kontexts. Ich habe weder die Zeit noch das Wissen, um eine Antwort darauf zu erstellen. Ich wollte es hier nur erwähnen, damit Leute, die diese Antwort suchen und unbefriedigend finden, einen anderen Thread ziehen können (Wortspiel beabsichtigt). In den meisten Fällen sollten Sie tun, was die akzeptierte Antwort nahelegt. Diese Antwort erwähnt Kontexte: stackoverflow.com/a/47302930/167958
Omnifarious
Antworten:
50
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 mainimport"sync"
func main(){var wg sync.WaitGroup
wg.Add(1)
ch := make(chan int)
go func(){for{
foo, ok :=<- chif!ok {
println("done")
wg.Done()return}
println(foo)}}()
ch <-1
ch <-2
ch <-3
close(ch)
wg.Wait()}
Der Körper der inneren Goroutine ist idiomatischer geschrieben, indem deferaufgerufen wird wg.Done(), und eine range chSchleife, um alle Werte zu durchlaufen, bis der Kanal geschlossen wird.
Alan Donovan
115
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:returndefault:// Do other stuff}}}()// Do stuff// Quit goroutine
quit <-true
Nicht gut genug. Was ist, wenn die Goroutine aufgrund eines Fehlers in einer Endlosschleife steckt?
Elazar Leibovich
232
Dann sollte der Fehler behoben sein.
Jimt
13
Elazar, Sie schlagen vor, eine Funktion zu stoppen, nachdem Sie sie aufgerufen haben. Eine Goroutine ist kein Faden. Es kann in einem anderen Thread oder in demselben Thread wie Ihres ausgeführt werden. Ich kenne keine Sprache, die das unterstützt, was Go Ihrer Meinung nach unterstützen sollte.
Jeremy Wall
5
@jeremy Sie sind nicht anderer Meinung als Go, aber mit Erlang können Sie einen Prozess beenden, auf dem eine Schleifenfunktion ausgeführt wird.
MatthewToday
10
Go Multitasking ist kooperativ und nicht präventiv. Eine Goroutine in einer Schleife tritt niemals in den Scheduler ein, sodass sie niemals getötet werden kann.
Jeff Allen
34
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.
Vielleicht möchten Sie sich auch das Paket encoding / gob ansehen, mit dem zwei Go-Programme problemlos Datenstrukturen über eine Pipe austauschen können.
Jeff Allen
In meinem Fall habe ich eine Goroutine, die bei einem Systemaufruf blockiert wird, und ich muss sie anweisen, den Systemaufruf abzubrechen und dann zu beenden. Wenn ich auf einem Kanal gelesen würde, wäre es möglich, das zu tun, was Sie vorschlagen.
Omnifarious
Ich habe dieses Problem schon einmal gesehen. Die Art und Weise, wie wir es "gelöst" haben, bestand darin, die Anzahl der Threads zu Beginn der Anwendung zu erhöhen, um sie an die Anzahl der Goroutinen anzupassen, die möglicherweise + die Anzahl der CPUs erreichen
könnten
18
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 mainimport"fmt"import"time"
func do_stuff()int{return1}
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 mainimport("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{}{}returndefault:
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 mainimport("launchpad.net/tomb""time""fmt")
type Procstruct{Tomb tomb.Tomb}
func (proc *Proc)Exec(){
defer proc.Tomb.Done()// Must call only oncefor{select{case<-proc.Tomb.Dying():returndefault:
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
Dieses Paket ist sehr interessant! Haben Sie getestet, was tombmit 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()...
Gwyneth Llewelyn
1
Hallo Gwyneth, 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. "
Kevin Cantwell
7
Persönlich möchte ich die Reichweite eines Kanals in einer Goroutine verwenden:
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):
quelle
defer
aufgerufen wirdwg.Done()
, und einerange 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.
quelle
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.
quelle
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 Demo
context.WithCancel
Die erste Demo, verwenden Sie
channel
:Die zweite Demo verwenden
context
:quelle
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:
Die Ausgabe sollte folgendermaßen aussehen:
quelle
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ögerteproc.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 .
quelle