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
ch := make(chan int) // canale di int, unbuffered
go func() {
ch <- 42 // invia (può bloccare)
}()
v := <-ch // ricevi (può bloccare)
fmt.Println(v) // 42Składnia:
make(chan T)lubmake(chan T, N)dla buforowanego.ch <- vwysyła.v := <-chodbiera.v, ok := <-chodbiera z flagą (ok = falsejeś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
ch := make(chan int, 3)
ch <- 1 // non blocca
ch <- 2 // non blocca
ch <- 3 // non blocca
ch <- 4 // BLOCCA: il buffer è pienoNadawca 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:
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:
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 naresults chan<- R. - Rurociąg: etapy połączone kanałami.
- Sygnał zakończenia: kanał
chan struct{}zamknięty, aby zasygnalizować „stop”.
done := make(chan struct{})
go func() {
<-done // blocca finché qualcuno chiude done
cleanup()
}()
close(done) // segnale di stopchan struct{} to kanał „sygnałowy”: po prostu zero bajtów ładunku
synchronizacja.
Spróbuj
Utwórz niebuforowany kanał int ch, wyślij 42 z goroutine, odbierz w głównym i wydrukuj.
Pokaż wskazówkę
Bez bufora nadawca i odbiorca synchronizują się.
Rozwiązanie dostępne po 3 próbach
Wyślij 1,2,3 przez buforowany kanał, zamknij go, a następnie wydrukuj wszystko za pomocą for-range.
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
Co robi kanał NIEBUFFEROWANY, gdy nadawca wysyła, ale żaden odbiorca nie jest gotowy?
ch := make(chan int)
ch <- 1 // ?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 rangezuż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.