Home Arrow Icon Knowledge base Arrow Icon Global Arrow Icon Jak wewnętrznie operacje kanału rąk w czasie wykonywania czasu wykonania


Jak wewnętrznie operacje kanału rąk w czasie wykonywania czasu wykonania


Go Runtime Runtime obsługuje operacje kanałów wewnętrznie poprzez wyrafinowaną koordynację struktur danych, mechanizmów blokujących, harmonogramu Goroutine i synchronizacji w celu zapewnienia wydajnej, sprawiedliwej i pozbawionej impasów komunikacji między jeżoutinami.

Kanały w Go są przede wszystkim wdrażane jako struktura danych `hchan`, która zawiera stan kanału, w tym bufor, kolejki czekających nadawców i odbiorników, prymitywy synchronizacji i meta informacyjne, takie jak to, czy kanał jest zamknięty. Po utworzeniu kanału instancja „hchan` jest przydzielana na stercie, a wartość reprezentująca punkty kanału do tej struktury, umożliwiając wiele gorutin na odniesienie i korzystanie z niego jednocześnie.

U podstaw kanał składa się z:

- Okrągły bufor dla kanałów buforowanych, reprezentowany przez plasterek i indeksy `sendx` i` recvx`, które śledzą odpowiednio, gdzie wysyłać i odbierać elementy. Rozmiar bufora jest ustalony na tworzeniu kanałów i działa jak kolejka, która przechowuje wysłane elementy, ale jeszcze nie otrzymane.
- Dwa kolejki FIFO Wait (listy połączone) do zarządzania Goroutinami zablokowanymi po wysłaniu („sendQ`) i odbieraniu („ recvq`). Te kolejek przechowują struktury „sudog”, które reprezentują zablokowaną goroutinę i powiązane dane dotyczące operacji kanałowych, takich jak wartość wysyłania lub wskaźnik do miejsca, w którym można otrzymać wartość.
- Blokada Mutex w celu ochrony równoczesnego dostępu do wewnętrznych struktur kanału, zapewniając, że wysyłanie operacji i odbierania utrzymują spójność i nie uszkodzą stanu kanału w ramach równoczesnego dostępu.

kanałowa operacja wysyłania

Kiedy Goroutine próbuje wysłać wartość do kanału (`` działanie chory

Operacja odbierania (`` Lexity:

1. Pozyskiwanie blokady: Mutex kanału jest zablokowany.

2. Sprawdź, czy oczekują nadawcy: jeśli jest nadawca w „sendq`:
- Odbiornik przyjmuje wartość bezpośrednio z „sudog” nadawcy.
- nadawca jest odrzucony i oznaczony do runda.
- Obie goroutiny przechodzą natychmiast bez buforowania.

3. Sprawdź bufor dla kanałów buforowanych: jeśli nie czeka nadawca:
- Kanał sprawdza, czy jego bufor zawiera jakiekolwiek elementy.
- Jeśli tak, element jest kopiowany z pozycji bufora `BUF [RECVX]`.
- Wskaźnik „recvx` jest zwiększany, a` qCount` jest zmniejszany.
- Zamek jest zwolniony, a odbiornik trwa bez blokowania.

4. Blokowanie odbiornika: jeśli bufor jest pusty i żaden nadawca nie czeka:
- Odbiornik Goroutine jest reprezentowany jako „sudog”.
- Jest zakochany w „recvq`.
- Odbiornik jest zaparkowany przez harmonogram, dopóki operacja wysyłania go odblokuje.

5. Otrzymuje kanał zamknięty: jeśli kanał jest zamknięty, a bufor jest pusty:
- Otrzymuje zwrócenie wartości zerowej typu elementu.
- Odbiorniki nie blokują i mogą wykryć stan zamknięty przez drugą boolan zwróconą z operacji odbierania.

6. Uczciwość i zamówienie: Odbiorniki czekające są odrzucane FIFO w celu zachowania uczciwości, a planowanie szanuje ograniczenia zamawiania, ale nie gwarantuje ścisłej kolejności terminowej z powodu zachowania harmonogramu.

parking i integracja planowania

Kanały są ściśle zintegrowane z Goroutine Scheduler Go, który zarządza cyklami życia Goroutine przy użyciu trzech podmiotów:

- G (Goroutine): Lekki wątek na poziomie użytkownika.
- M (maszyna): Wątek OS, który wykonuje Goroutines.
- P (procesor): posiada lokalną kolejkę Runnable Goroutines i zasoby potrzebne do wykonania kodu GO.

Gdy operacja kanału blokuje (wysyła lub odbieraj), Goroutine:

- jest oznaczony jako czekanie.
- jest usuwany z lokalnej kolejki biegu własności „p”.
- jest powiązany przez strukturę „sudog` w odpowiedniej kolejce kanału (` `sendq` lub„ recvq`).
- Następnie harmonogram wybiera kolejną Runnable Goroutine, która będzie działać na tym „p`.

Kiedy zablokowana operacja stanie się gotowa do zakończenia (np. Dotyczy wysyłania lub odbierania odpowiednika):

- Oczekiwanie Goroutine jest odrzucane z kolejki kanału.
- Oznaczony runda.
- Umieszczony z powrotem na lokalnej lub globalnej kolejce do planowania.
- Goroutine ostatecznie wznowi wykonanie.

Ten projekt unika zajęty oczekiwaniem i zapewnia wydajne przełączanie kontekstu przy minimalnym kosztach ogólnych w porównaniu z tradycyjnym blokowaniem nici systemu operacyjnego.

Operacja zamknięcia kanału

Zamknięcie kanału ustawia flagę „zamkniętą” w „hchan” chronionym przez ten sam Mutex. Zamknięcie kanału powoduje następujące czynności:

- Wszystkie zablokowane nadawcy panika w przypadku próby wysyłania.
- Wszystkie zablokowane odbiorniki są odblokowane w kolejności FIFO, aby otrzymać pozostałe wartości buforowane, a następnie zerowe wartości.
- Ponadto odbiera zwroty wartości zero natychmiast bez blokowania.
- Natychmiast wysyłanie paniki kanału zamkniętego.
- Operacja zamknięcia jest również synchronizowana z operacjami kanałów przez mutex, aby uniknąć warunków rasowych.

Wewnętrzne struktury danych

- Hchan: Central STRUCT Holding:
- `buf` (wskaźnik tablicy bufora)
- `DataQsiz` (rozmiar bufora)
- `qCount` (liczba elementów obecnie buforowanych)
- `sendx`,` recvx` (wskaźniki bufora)
- `sendq`,` recvq` (kolejki poczekaj na zablokowane nadawcy i odbiorniki)
- Flaga „zamknięta”
- Wbudowany mutx (w celu ochrony stanu i koordynowania wielu współbieżnych operacji).

- Sudog: Wewnętrzna struktura środowiska wykonawczego reprezentująca Goroutinę czekającą na operację kanału. Holds:
- Wskaźnik do Goroutine.
- Wskaźniki do wysyłanych lub odbieranych danych.
- Linki do następnego „sudog” w kolejce oczekiwania.

- Waitq: Lista linkowanej sudogów reprezentujących albo Wysyłanie kelnerów (`sendq`) lub odbiorców (` `recvq`). Zapewnienie uczciwości FIFO.

Wybierz instrukcję i kanały

Oświadczenie Go „Select” wprowadza złożoność, ponieważ wiele operacji kanałowych konkuruje jednocześnie. Runtime zarządza wieloma kolejkami oczekiwania na każdą wybraną skrzynkę, a wybór z wielu kanałów offline obejmuje:

- Wypróbowanie operacji nieoblokowania w każdej sprawie.
- Jeśli żaden nie kontynuuje, obejmuje go -goroutinę we wszystkich odpowiednich kolejkach.
- Gdy jedna operacja może kontynuować, inni kelnerzy zostaną usunięci, a Goroutine odblokowuje.

Ten mechanizm selektywnego oczekiwania jest głęboko zintegrowany z harmonogramem czasu wykonywania w celu zachowania uczciwości i wydajności.

Rozważania dotyczące wydajności i współbieżności

Projekt kanału dąży do równowagi między bezpieczeństwem a wydajnością:

- Mutex Blokowanie: Zapewnia spójność, ale może stać się wąskim gardłem przy bardzo wysokim rywalizacji. Środek wykonawczy utrzymuje blokadę, aby krótko aktualizować stan atomowo.
- Bezpośrednie przekaz: Unikaj ogółu bufora, jeśli to możliwe, umożliwiając szybszą komunikację między Goroutinami.
- Parking i niezmienne: minimalizuje przełączanie kontekstowe narzutowe w porównaniu do wątków systemu operacyjnego.
- Uczciwość: egzekwowana przez kolejki FIFO, aby zapobiec głodu.
- pamięć podręczna i lokalizacja: Nieprzewodowane Goroutines mogą wznowić na różnych procesorach („P'S), prawdopodobnie prowadząc do braków pamięci podręcznej.
- Problem z grzmotem: Zamknięcie kanału lub transmisja może jednocześnie obudzić wiele Goroutines, które harmonogramu muszą być rozsądnie.

Kanały reprezentują elegancką współbieżną prymitywną, która łączy bufor pierścieniowy chronionego blokadą z mechanizmami planowania wyrobów, zapewniający bezpieczeństwo, zamawianie i wydajność programów GO.

Wewnętrzne operacje kanału GO ujawniają ścisłe sprzężenie między strukturami danych, blokowaniem i harmonogramem czasu wykonywania, osadzanie synchronizacji w lekkich wątkach na poziomie użytkownika i umożliwiając bogate wzorce komunikacji bez interakcji z jądrem systemu operacyjnego. Takie podejście odróżnia model współbieżności GO i jest kluczowym powodem, dla którego kanały są zarówno wydajne, jak i proste w użyciu z perspektywy programisty.