ProgrammatieMiddle Backend Go ontwikkelaar

Wat gebeurt er bij het verzenden van waarden via een kanaal in Go: hoe werkt het kopiëren, welke types worden per waarde verzonden, hoe kun je per ongeluk een race condition krijgen bij het verzenden van complexe structuren of pointers?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

In Go is het mogelijk om waarden van elk type via kanalen te verzenden: int, struct, pointers, interfaces, enz.

  • Verzending «per waarde»: standaardtypen en structuren (zonder pointers) worden gekopieerd, de ontvanger ontvangt zijn kopie en wijzigingen hebben geen invloed op het origineel.
  • Race-controle: als een pointer via het kanaal is verzonden, werken beide zijden (de verzender en de ontvanger) met hetzelfde geheugen — een data race is mogelijk!
  • Complexe structuren met geneste pointers: zelfs als de hoofdstructuur per waarde wordt verzonden, worden de pointers die erin zijn genest als verwijzingen gekopieerd, en een race is mogelijk op het niveau van geneste objecten.

Code en voorbeeld:

type Data struct { N int } c := make(chan Data) d := Data{N: 1} c <- d // de structuur wordt volledig gekopieerd p := &Data{N: 3} c2 := make(chan *Data) c2 <- p // een pointer naar hetzelfde object werd via het kanaal verzonden

Strikvraag

Als je een structuur via een kanaal verzendt die een pointer-veld bevat — zal er een race condition optreden bij het wijzigen van dit veld aan beide zijden van het kanaal?

Antwoord:

  • Race is mogelijk! De structuur wordt gekopieerd, maar de geneste pointer verwijst naar hetzelfde geheugen. Als beide zijden de gegevens via de verwijzing wijzigen, ontstaat er een race condition.

Voorbeeld:

type Box struct { Ptr *int } x := 10 chanBox := make(chan Box) chanBox <- Box{Ptr: &x} // zowel de verzender als de ontvanger hebben toegang tot x!

Voorbeelden van echte fouten door onwetendheid over de fijnere punten van het onderwerp


Verhaal

Er waren frequente "toevallige" crashes en mysterieuze waarden in de taakstructuur in een gedistribueerde wachtrij. Het bleek dat pointers naar gemeenschappelijke structuren via het kanaal werden verzonden, die gelijktijdig in meerdere goroutines werden gewijzigd. We hebben besloten om te switchen naar het verzenden van kopieën van de gegevens.


Verhaal

Asynchrone berichtverwerking werkte met structuren die slices-pointers naar een gemeenschappelijkArray bevatten. Bij gelijktijdige verzending via het kanaal werden delen van hetzelfde geheugen vanuit verschillende plaatsen gewijzigd, wat leidde tot obscure bugs en gegevenscorruptie.


Verhaal

In de push-notificatiedienst werden er pointers naar objecten via het kanaal verzonden, die vervolgens door goroutines werden verwerkt. Bij gelijktijdige beëindiging en sluiting van het kanaal werden delen van structuren nog steeds gewijzigd, wat paniek of een data race veroorzaakte. De overgang naar het kopiëren van structuren vóór verzending hielp.