ProgrammatieBackend Ontwikkelaar

Hoe zijn closures in Go geïmplementeerd, welke valkuilen zijn verbonden aan het gebruik ervan bij het starten van goroutines binnen loops, en hoe deze typische fouten te vermijden?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

In Go zijn closures functies die variabelen uit hun omliggende scope "vastleggen" (capturing). Het meest gebruikelijke gebruik van closures is voor anonieme functies die binnen andere functies worden gemaakt.

De meest typische probleem bij het werken met closures is onverwacht gedrag bij het gebruik van loopvariabelen binnen een goroutine:

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

Elke goroutine kan dezelfde waarde van i afdrukken, omdat de variabele i blijft loop-en het closure legt de variabele vast, niet de waarde ervan bij elke iteratie.

De juiste manier:

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

Dit gedrag komt doordat het closure een referentie naar de variabele (de adres) vasthoudt, en niet haar gesneden (by value) waarde.

Vrag met een valstrik

Welke waarde zullen meerdere gestart goroutines binnen een loop afdrukken als ze de loopvariabele vastleggen?

Antwoord: Alle goroutines kunnen dezelfde waarde afdrukken (vaak de laatste), omdat ze de huidige waarde van de variabele zien, en tegen de tijd dat de goroutine uitgevoerd wordt, de loop al is beëindigd. Om dit te vermijden, moet de variabele als parameter aan het closure worden doorgegeven.

Voorbeeld:

for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // waarschijnlijk krijgen we: 5 5 5 5 5

Voorbeelden van echte fouten door onwetendheid van de nuances van het onderwerp


Verhaal

In een productanalysesysteem werden gegevens geanonimiseerd in parallelle goroutines met behulp van een closure dat de loopvariabele vastlegde. Het gevolg was dat alle parallelle taken dezelfde dataset verwerkten — het resultaat was een vertekening van de statistieken en onjuiste rapportage.

Verhaal

In een cloudservice backend Go-integraties besloten teams de verzameling van metrics te optimaliseren door deze verwerking in een loop te starten met behulp van anonieme functies — binnen de goroutine legden ze de index van de map vast, resultaat: een deel van de handlers verzamelde gegevens niet voor hun services, maar voor de laatst verwerkte index.

Verhaal

In een koeriersstartup leidde onjuist gebruik van closures bij het bijwerken van de coördinaten van een bestelling ertoe dat massaal de coördinaten van de laatste bestelling in de slice werden bijgewerkt, en niet de huidige — door een race bij toegang tot de slice binnen de anonieme functie.