Go Frequently Asked Questions (FAQ)
What happens with closures running as goroutines?
Einige Verwirrung entstehen, wenn Verschlüsse mit gemeinsamen Zugriff verwenden. Betrachten Sie das folgende Programm:
func main() {
done := make(chan bool)
values := []string{"a", "b", "c"}
for _, v := range values {
go func() {
fmt.Println(v)
done <- true
}()
}
// wait for all goroutines to complete before exiting
for _ = range values {
<-done
}
}
man erwarten könnte fälschlicherweise ein, um zu sehen, b, c als Ausgang. Was Sie wahrscheinlich sehen werden, ist c, c, c. Dies liegt daran, dass für jede Iteration der Schleife die gleiche Instanz der Variablen v verwendet wird, so dass jeder Abschluss diese einzelne Variable gemeinsam nutzt. Wenn der Abschluss ausgeführt wird, wird der Wert von v zu dem Zeitpunkt ausgegeben, zu dem fmt.Println ausgeführt wird, aber v wurde möglicherweise seit dem Start der Goroutine geändert. Um zu helfen, diese und andere Probleme zu erkennen, bevor sie passieren, führen Sie go vet.
Um den aktuellen Wert von v an jedes Schließelement beim Start zu binden, muss eine die innere Schleife ändern, um bei jeder Iteration eine neue Variable zu erstellen. Eine Möglichkeit ist die Variable als ein Argument an den Verschluss passieren:
for _, v := range values {
go func(u string) {
fmt.Println(u)
done <- true
}(v)
}
In diesem Beispiel ist der Wert von v wird als Argument an die anonyme Funktion übergeben. Dieser Wert ist dann innerhalb der Funktion als Variable u zugänglich.
Noch einfacher ist nur eine neue Variable zu erstellen, eine Erklärung Stil verwenden, das mag seltsam erscheinen, aber funktioniert in Go fein:
for _, v := range values {
v := v // create a new 'v'.
go func() {
fmt.Println(v)
done <- true
}()
}
einfach eine neue Variable für die Schließung erstellen mit ein Deklarations-Stil, der seltsam erscheinen mag, aber in Go gut funktioniert. Fügen Sie task := task
hinzu. Zum Beispiel
Danke, die dritte ist besser, weil ich das Mitglied der Struktur ändern möchte. – Devin
@Devin, dritte Option ist die einzige Möglichkeit, wenn Sie das Segment ändern möchten. Bitte beachten Sie die aktualisierte dritte Option. Es ist weniger ausführlich als das Original. –
@Devin Erste Option würde auch normalerweise funktionieren (solange Ihre Absicht darin besteht, die 'Task' zu ändern und den Zeiger im Slice nicht durch einen neuen Zeiger zu ersetzen), aber die dritte Option ist noch besser. – hobbs