programowanieŚredni programista Backend Go

Co się dzieje, gdy przekazywane są wartości przez kanał w Go: jak działa kopiowanie, jakie typy są przekazywane przez wartość, jak można przypadkowo uzyskać race przy przekazywaniu złożonych struktur lub wskaźników?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W Go można przesyłać przez kanały wartości dowolnego typu: int, struct, wskaźniki, interfejsy itd.

  • Przekazywanie „przez wartość”: standardowe typy i struktury (bez wskaźników) są kopiowane, odbiorca dostaje swoją kopię, a zmiany nie wpływają na oryginał.
  • Sprawdzanie race: jeśli przez kanał przesyłany jest wskaźnik, obie strony (nadawca i odbiorca) pracują na tej samej przestrzeni pamięci — możliwy jest wyścig danych!
  • Złożone struktury z zagnieżdżonymi wskaźnikami: nawet jeśli główna struktura jest przekazywana przez wartość, zagnieżdżone w niej wskaźniki są kopiowane jako odniesienia i wyścig jest możliwy na poziomie zagnieżdżonych obiektów.

Kod i przykład:

type Data struct { N int } c := make(chan Data) d := Data{N: 1} c <- d // cała struktura jest kopiowana p := &Data{N: 3} c2 := make(chan *Data) c2 <- p // przez kanał przesyłany jest wskaźnik do tego samego obiektu

Pytanie z pułapką

Czy jeśli przez kanał przesłać strukturę zawierającą pole-wskaźnik — czy wystąpi race condition przy zmianie tego pola z obu końców kanału?

Odpowiedź:

  • Wyścig jest możliwy! Struktura jest kopiowana, ale zagnieżdżony wskaźnik odnosi się do tej samej przestrzeni pamięci. Jeśli obie strony modyfikują dane przez odniesienie, dochodzi do stanu wyścigu.

Przykład:

type Box struct { Ptr *int } x := 10 chanBox := make(chan Box) chanBox <- Box{Ptr: &x} // zarówno nadawca, jak i odbiorca mają dostęp do x!

Przykłady rzeczywistych błędów wynikających z braku znajomości niuansów tematu


Historia

W rozproszonej kolejce występowały częste "przypadkowe" awarie i tajemnicze wartości w strukturze zadania. Okazało się, że przez kanał przesyłano wskaźniki do wspólnych struktur, które były równolegle modyfikowane w kilku gorutynach. Postanowiono zmienić metodę na przesyłanie kopii danych.


Historia

Asynchroniczna obróbka wiadomości działała ze strukturami, w których były slice-wskaźniki na wspólną tablicę. Przy równoległym przesyłaniu przez kanał część tej samej pamięci była modyfikowana z różnych miejsc, co prowadziło do ukrytych błędów i uszkodzenia danych.


Historia

W serwisie powiadomień push przez kanał przesyłano wskaźniki do obiektów, które były następnie przetwarzane przez gorutyny. Przy jednoczesnym kończeniu pracy i zamykaniu kanału część struktur nadal była modyfikowana, co powodowało panikę lub race data. Pomogło przejście na kopiowanie struktury przed wysłaniem.