Przejdź do głównej treści
eLearner.app
Moduł 7 · Lekcja 2 z 532/50 w kursie~15 min
Lekcje modułu (2/5)

Kanały: typowana komunikacja

Kanały to idiomatyczny mechanizm komunikacji pomiędzy goroutiny. Filozofia Go (Rob Pike):

„Nie komunikuj się, dzieląc się pamięcią; udostępniaj pamięć, komunikując się”.

Kanał jest kolejką FIFO z typem, bezpieczną dla typu i bezpieczną dla współbieżności używać. W połączeniu z go stanowi podstawowy element składowy Go współbieżność.

Twórz, wysyłaj, odbieraj

Go
ch := make(chan int)     // canale di int, unbuffered

go func() {
    ch <- 42             // invia (può bloccare)
}()

v := <-ch                // ricevi (può bloccare)
fmt.Println(v)           // 42

Składnia:

  • make(chan T) lub make(chan T, N) dla buforowanego.
  • ch <- v wysyła.
  • v := <-ch odbiera.
  • v, ok := <-ch odbiera z flagą (ok = false jeśli kanał jest zamknięty ORAZ pusty).

Niebuforowane: spotkanie

Kanał niebuforowany (pojemność 0) blokuje:

  • nadawca do czasu, aż odbiorca będzie gotowy;
  • odbiorca do czasu, aż nadawca będzie gotowy.

Jest to spotkanie synchroniczne: przydatne jako „sygnał” lub gwarancja że wartość została dostarczona.

Buforowane: kolejka do N

Go
ch := make(chan int, 3)
ch <- 1   // non blocca
ch <- 2   // non blocca
ch <- 3   // non blocca
ch <- 4   // BLOCCA: il buffer è pieno

Nadawca blokuje tylko wtedy, gdy bufor jest pełny; tylko odbiornik kiedy jest pusto.

blisko i na odległość

Nadawca zamyka kanał, gdy nie ma już żadnych wartości:

Go
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)

for v := range ch {      // termina quando ch è chiuso E vuoto
    fmt.Println(v)
}

for range po kanale iteruje, aż kanał zostanie zamknięty (i opróżniony). Jest to najczystszy wzór na „konsumowanie wszystkiego, co nadejdzie”.

Kierunkowość w podpisach

Parametry funkcji mogą ograniczać kierunek:

Go
func producer(out chan<- int) {   // solo invio
    out <- 1
    close(out)
}

func consumer(in <-chan int) {    // solo ricezione
    for v := range in {
        fmt.Println(v)
    }
}

chan<- T = tylko wysyłanie, <-chan T = tylko odbieranie. Dwukierunkowy kanał chan T można przekazać do dowolnego z nich (konwersja niejawna). To poprawia przejrzystość i zapobiega błędom.

Typowe wzorce (podgląd)

  • Pula pracowników: N gorutyn konsumuje z jobs <-chan T, produkuje na results chan<- R.
  • Rurociąg: etapy połączone kanałami.
  • Sygnał zakończenia: kanał chan struct{} zamknięty, aby zasygnalizować „stop”.
Go
done := make(chan struct{})
go func() {
    <-done    // blocca finché qualcuno chiude done
    cleanup()
}()
close(done)   // segnale di stop

chan struct{} to kanał „sygnałowy”: po prostu zero bajtów ładunku synchronizacja.

Spróbuj

Ćwiczenie#go.m7.l2.e1
Próby: 0Ładowanie...

Utwórz niebuforowany kanał int ch, wyślij 42 z goroutine, odbierz w głównym i wydrukuj.

Ładowanie edytora...
Pokaż wskazówkę

Bez bufora nadawca i odbiorca synchronizują się.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#go.m7.l2.e2
Próby: 0Ładowanie...

Wyślij 1,2,3 przez buforowany kanał, zamknij go, a następnie wydrukuj wszystko za pomocą for-range.

Ładowanie edytora...
Pokaż wskazówkę

`for range` na kanale kończy się, gdy kanał jest zamknięty i opróżniony.

Rozwiązanie dostępne po 3 próbach

Quiz#go.m7.l2.e3
Gotowe

Co robi kanał NIEBUFFEROWANY, gdy nadawca wysyła, ale żaden odbiorca nie jest gotowy?

Go
ch := make(chan int)
ch <- 1 // ?
Opcje odpowiedzi

Podsumowanie

  • Kanał = wpisana kolejka FIFO, bezpieczna pomiędzy goroutinami.
  • Niebuforowane: spotkanie (synchronizacja nadawcy i odbiorcy).
  • Buforowane: oddzielanie, blokowanie tylko przy pełnym/pustym stanie.
  • close(ch): tylko nadawca; for range zużywa aż do zamknięcia.
  • Kierunkowość: chan<- T (tylko wysyłanie), <-chan T (tylko odbiór).
  • chan struct{} jako kanał sygnałowy dla czystej synchronizacji.