0Pricing
Go Academy · Lekcja

Kanały niebuforowane

Synchroniczne przekazywanie danych między goroutines

Kanały niebuforowane to bezpłatna lekcja Go Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Go Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Go Academy zawiera 4 lekcji w sumie.

Czym jest kanał?

Kanał to typowany mechanizm komunikacji między gorutynami. Kanały realizują zasadę Go: Nie komunikuj się przez współdzielenie pamięci; współdziel pamięć przez komunikację.

package main
import "fmt"

func main() {
    ch := make(chan int)   // unbuffered int channel

    go func() {
        ch <- 42  // send: blocks until someone receives
    }()

    v := <-ch  // receive: blocks until someone sends
    fmt.Println(v) // 42
}

Semantyka kanału niebuforowanego

Kanał niebuforowany nie ma pojemności. Wysyłanie blokuje się, dopóki odbiorca nie będzie gotowy, a odbiór blokuje się, dopóki nadawca nie wyśle wartości. Zapewnia to gwarantowane synchroniczne przekazanie:

package main
import "fmt"

func double(in <-chan int, out chan<- int) {
    for v := range in {
        out <- v * 2
    }
    close(out)
}

func main() {
    in := make(chan int)
    out := make(chan int)
    go double(in, out)
    go func() { in <- 3; in <- 5; close(in) }()
    for v := range out { fmt.Println(v) }
    // 6
    // 10
}

Kanał jako mechanizm synchronizacji

Kanały niebuforowane synchronizują gorutyny — należy używać ich jako sygnału zakończenia pracy:

package main
import "fmt"

func compute(result chan<- int) {
    sum := 0
    for i := 1; i <= 100; i++ { sum += i }
    result <- sum
}

func main() {
    ch := make(chan int)
    go compute(ch)
    fmt.Println("Result:", <-ch) // blocks until compute sends
}

Zamykanie kanału

Nadawca zamyka kanał za pomocą close(ch), aby zasygnalizować, że nie zostaną wysłane żadne kolejne wartości. Odbiorcy wykrywają zamknięcie za pomocą postaci z dwiema wartościami:

package main
import "fmt"

func generate(ch chan<- int, max int) {
    for i := 0; i < max; i++ {
        ch <- i
    }
    close(ch)  // signal done
}

func main() {
    ch := make(chan int)
    go generate(ch, 5)

    for v := range ch {  // range detects close automatically
        fmt.Println(v)
    }
    fmt.Println("channel closed")
}

Odbiór dwóch wartości

Odbiór dwóch wartości v, ok := <-ch informuje, czy kanał jest nadal otwarty:

package main
import "fmt"

func main() {
    ch := make(chan string, 1)
    ch <- "hello"
    close(ch)

    v1, ok1 := <-ch
    fmt.Println(v1, ok1)  // hello true

    v2, ok2 := <-ch
    fmt.Println(v2, ok2)  // "" false — channel closed
}

Wysyłanie do zamkniętego kanału wywołuje panikę

Wysłanie wartości do zamkniętego kanału wywołuje panikę. Tylko nadawca powinien zamykać kanał — nigdy odbiorca:

package main
import "fmt"

func main() {
    ch := make(chan int)
    close(ch)

    // Receiving from closed channel returns zero value
    v, ok := <-ch
    fmt.Println(v, ok)  // 0 false

    // Sending to closed channel panics:
    defer func() { recover() }()
    ch <- 1  // panic: send on closed channel
}

Kanały kierunkowe

Dla większej przejrzystości i bezpieczeństwa można ograniczyć kanały w sygnaturach funkcji do kanałów tylko do wysyłania (chan<- T) lub tylko do odbierania (<-chan T):

package main
import "fmt"

func producer(out chan<- int) {  // send-only
    for i := 0; i < 3; i++ { out <- i }
    close(out)
}

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

func main() {
    ch := make(chan int)
    go producer(ch)
    consumer(ch)
}

Ping-pong gorutyn i kanałów

Dwie gorutyny przekazujące sobie wiadomość za pośrednictwem kanałów — klasyczny przykład synchronizacji:

package main
import "fmt"

func ping(ping, pong chan struct{}) {
    for i := 0; i < 3; i++ {
        <-ping
        fmt.Println("ping")
        pong <- struct{}{}
    }
}

func main() {
    ping := make(chan struct{})
    pong := make(chan struct{})
    go func() { for { <-pong; fmt.Println("pong"); ping <- struct{}{} } }()
    go func() { for i := 0; i < 3; i++ { ping <- struct{}{}; <-pong } }()
    // Simplified version:
    fmt.Println("channels enable structured communication")
}

Kanały nil blokują na zawsze

Odbieranie z kanału nil lub wysyłanie do niego blokuje na zawsze. Czasami wykorzystuje się to celowo w instrukcjach select:

package main
import "fmt"

func main() {
    var ch chan int  // nil channel
    fmt.Println(ch == nil) // true

    // select with nil channel — that case is never selected
    active := make(chan int, 1)
    active <- 42
    select {
    case v := <-ch:      // never selected (nil)
        fmt.Println(v)
    case v := <-active:  // selected
        fmt.Println("got:", v)
    }
}

Kiedy używać kanałów niebuforowanych

Kanałów niebuforowanych należy używać, gdy:

  • Potrzebne jest gwarantowane synchroniczne przekazanie
  • Gorutyna musi potwierdzić odbiór, zanim nadawca będzie kontynuować działanie
  • Implementowane są semafory lub mechanizm back-pressure
  • Tworzone są potoki, w których każdy etap powinien przetwarzać jeden element naraz

Szybki test

Co się dzieje, gdy nastąpi próba wysłania do niebuforowanego kanału, gdy żaden odbiorca nie jest gotowy?

Podsumowanie: kanały niebuforowane

Podsumowanie:

  • Tworzenie za pomocą make(chan T) — zerowa pojemność
  • Wysyłanie blokuje się do chwili gotowości odbiorcy, a odbiór — do chwili wysłania wartości przez nadawcę
  • Tylko nadawca powinien zamykać kanał
  • range ch iteruje do zamknięcia kanału
  • Odbiór dwóch wartości: v, ok := <-ch
  • Dla większej przejrzystości należy używać typów kierunkowych: chan<- i <-chan

Często zadawane pytania

Czy lekcja „Kanały niebuforowane” jest bezpłatna?

Tak — pełny tekst „Kanały niebuforowane” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Go Academy, przejdź na CoddyKit PRO. Kurs Go Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Kanały niebuforowane”?

Synchroniczne przekazywanie danych między goroutines Ćwiczysz Go Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Go Academy?

Nie wymagamy żadnego doświadczenia. Go Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Kanały niebuforowane”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Go Academy?

Tak. Każda lekcja Go Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Uruchamianie goroutines
  2. Kanały niebuforowane
  3. Kanały buforowane
  4. Kierunek kanałów i wzorce potoków
← Powrót do Go Academy