ProgrammierungBackend-Entwickler

Wie werden Closures in Go implementiert, welche Fallstricke sind mit ihrer Verwendung beim Starten von Goroutinen innerhalb von Schleifen verbunden, und wie kann man typische Fehler vermeiden?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

In Go sind Closures Funktionen, die Variablen aus ihrer umgebenden Sichtbarkeit "einfangen". Am häufigsten werden Closures für anonyme Funktionen verwendet, die innerhalb anderer Funktionen erstellt werden.

Das häufigste Problem bei der Arbeit mit Closures ist das unerwartete Verhalten beim Einsatz von Schleifenvariablen innerhalb einer Goroutine:

for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }

Jede Goroutine kann denselben Wert von i ausgeben, weil die Variable i läuft und das Closure die Variable selbst und nicht ihren Wert in jeder Iteration einfängt.

Richtiger Weg:

for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }

Dieses Verhalten ist darauf zurückzuführen, dass das Closure einen Verweis auf die Variable (ihre Adresse) und nicht ihren geschnittenen (by value) Wert hält.

Fangfrage

Welchen Wert geben mehrere gestartete Goroutinen innerhalb einer Schleife aus, wenn sie die Schleifenvariable einfangen?

Antwort: Alle Goroutinen können denselben Wert ausgeben (häufig den letzten), da sie den aktuellen Wert der Variablen sehen und zu dem Zeitpunkt, an dem die Goroutine ausgeführt wird, die Schleife bereits abgeschlossen ist. Um dies zu vermeiden, muss die Variable als Parameter an das Closure übergeben werden.

Beispiel:

for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // höchstwahrscheinlich erhalten wir: 5 5 5 5 5

Beispiele für reale Fehler aufgrund von Unkenntnis der Feinheiten des Themas


Geschichte

In einem produktiven Analysesystem wurden Daten in parallelen Goroutinen anonymisiert, indem das Closure die Schleifenvariable einfing. Infolgedessen bearbeiteten alle parallelen Aufgaben denselben Datensatz – das Ergebnis war eine Verzerrung der Statistiken und eine fehlerhafte Berichterstattung.

Geschichte

In einem Cloud-Service für Go-Integrationen entschieden die Teams, die Sammlung von Metriken zu optimieren, indem sie deren Verarbeitung in einer Schleife mit anonymen Funktionen starteten – innerhalb der Goroutine wurde der Index der Map eingefangen, das Ergebnis: Ein Teil der Handler sammelte Daten nicht für ihre eigenen Services, sondern für den zuletzt bearbeiteten Index.

Geschichte

In einem Kurier-Startup führte die fehlerhafte Verwendung von Closures beim Aktualisieren der Bestellkoordinaten dazu, dass massenhaft die Koordinaten der letzten Bestellung im Slice aktualisiert wurden, anstatt der aktuellen – aufgrund eines Rennens beim Zugriff auf das Slice innerhalb der anonymen Funktion.