Go Academy · Lekcja

Kierunek kanałów i wzorce potoków

Kanały tylko do odczytu, tylko do zapisu i potoki

Lekcja 4 z 412 kroki

Kierunek kanałów i wzorce potoków to bezpłatna lekcja Go Academy na CoddyKit. To lekcja 4 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.

Kierunkowe typy kanałów

Go pozwala ograniczyć kanały w sygnaturach typów do kanałów tylko do wysyłania lub tylko do odbierania. Dokumentuje to przeznaczenie i zapobiega niewłaściwemu użyciu:

package main

// send-only: can only send, not receive or close (close is allowed by sender)
func sender(out chan<- int) {
    out <- 42
}

// receive-only: can only receive, not send or close
func receiver(in <-chan int) int {
    return <-in
}

// bidirectional: can do both
func relay(in <-chan int, out chan<- int) {
    out <- <-in
}

Konwertowanie kierunku kanałów

Kanał dwukierunkowy można niejawnie przekonwertować na dowolny z typów kierunkowych, ale konwersja w drugą stronę nie jest możliwa:

package main
import "fmt"

func produce(out chan<- int) { out <- 1; close(out) }
func consume(in <-chan int)  { fmt.Println(<-in) }

func main() {
    ch := make(chan int)   // bidirectional
    go produce(ch)         // auto-converts to chan<- int
    consume(ch)            // auto-converts to <-chan int
    // chan<- int cannot be converted back to <-chan int
}

Potok: etap 1 — generator

Potok łączy gorutyny, w którym każdy etap odczytuje dane z kanału wyjściowego poprzedniego etapu. Generator jest pierwszym etapem:

package main
import "fmt"

func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func main() {
    ch := generate(2, 3, 4, 5)
    for v := range ch { fmt.Println(v) }
}

Potok: etap 2 — transformacja

Etapy pośrednie odbierają dane z jednego kanału, przekształcają je i wysyłają do innego:

package main
import "fmt"

func generate(nums ...int) <-chan int {
    out := make(chan int)
    go func() { for _, n := range nums { out <- n }; close(out) }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for v := range in { out <- v * v }
        close(out)
    }()
    return out
}

func main() {
    for v := range square(generate(2, 3, 4, 5)) {
        fmt.Println(v)  // 4, 9, 16, 25
    }
}

Potok: pełny przykład z trzema etapami

Połączenie trzech etapów — generowania, transformacji i filtrowania:

package main
import "fmt"

func gen(nums ...int) <-chan int {
    c := make(chan int)
    go func() { for _, n := range nums { c <- n }; close(c) }()
    return c
}

func double(in <-chan int) <-chan int {
    c := make(chan int)
    go func() { for v := range in { c <- v * 2 }; close(c) }()
    return c
}

func evens(in <-chan int) <-chan int {
    c := make(chan int)
    go func() { for v := range in { if v%2 == 0 { c <- v } }; close(c) }()
    return c
}

func main() {
    for v := range evens(double(gen(1,2,3,4,5))) {
        fmt.Println(v)  // 2, 4, 6, 8, 10
    }
}

Anulowanie potoku za pomocą kanału done

Należy przekazać kanał done, aby etapy potoku mogły zakończyć działanie wcześniej po anulowaniu:

package main
import "fmt"

func gen(done <-chan struct{}, nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums {
            select {
            case out <- n:
            case <-done:  // cancel signal
                return
            }
        }
    }()
    return out
}

func main() {
    done := make(chan struct{})
    ch := gen(done, 1, 2, 3, 4, 5)
    fmt.Println(<-ch)  // 1
    close(done)        // cancel remaining work
    fmt.Println("pipeline cancelled")
}

Fan-out: rozdzielanie pracy

Fan-out rozdziela pracę z jednego kanału między wiele gorutyn w celu równoległego przetwarzania:

package main
import ("fmt"; "sync")

func fanOut(in <-chan int, workers int) []<-chan int {
    outs := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        out := make(chan int)
        outs[i] = out
        go func() {
            for v := range in { out <- v * v }  // each worker processes
            close(out)
        }()
    }
    return outs
}

func main() {
    fmt.Println("fan-out distributes work to multiple goroutines")
    _ = sync.WaitGroup{}
}

Fan-in: scalanie kanałów

Fan-in scala wiele kanałów w jeden:

package main
import ("fmt"; "sync")

func merge(cs ...<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    for _, c := range cs {
        wg.Add(1)
        c := c
        go func() { defer wg.Done(); for v := range c { out <- v } }()
    }
    go func() { wg.Wait(); close(out) }()
    return out
}

func gen(nums ...int) <-chan int {
    c := make(chan int)
    go func() { for _, n := range nums { c <- n }; close(c) }()
    return c
}

func main() {
    for v := range merge(gen(1,2), gen(3,4), gen(5,6)) {
        fmt.Println(v)
    }
}

Najlepsze praktyki projektowania potoków

Zasady projektowania potoków:

  • Każdy etap: gorutyna odczytuje z <-chan, zapisuje do chan<- i po zakończeniu zamyka kanał wyjściowy
  • Tylko producent zamyka kanał — konsument nigdy
  • Należy przekazywać kanał done (lub kontekst) w celu anulowania
  • We wszystkich sygnaturach funkcji należy używać kierunkowych typów kanałów
  • Po anulowaniu należy opróżniać kanały, aby zapobiec wyciekom gorutyn

Potok w praktyce: przetwarzanie plików CSV

Potoki doskonale sprawdzają się w przetwarzaniu danych — odczytywaniu wierszy, analizowaniu, filtrowaniu i przekształcaniu:

package main
import ("fmt"; "strings")

func splitLines(data string) <-chan string {
    out := make(chan string)
    go func() {
        for _, line := range strings.Split(data, "\n") {
            if line != "" { out <- line }
        }
        close(out)
    }()
    return out
}

func main() {
    csv := "alice,30\nbob,25\ncarol,35"
    for line := range splitLines(csv) {
        parts := strings.Split(line, ",")
        fmt.Printf("name=%s age=%s\n", parts[0], parts[1])
    }
}

Szybkie sprawdzenie

Jaki jest cel używania kierunkowych typów kanałów w sygnaturach funkcji?

Podsumowanie: kierunek kanałów i potoki

Podsumowanie:

  • chan<- T tylko do wysyłania, <-chan T tylko do odbierania — jawnie określona intencja i bezpieczeństwo sprawdzane podczas kompilacji
  • Potoki: łańcuchy gorutyn, w których każda odczytuje z jednego kanału i zapisuje do innego
  • Fan-out: jeden kanał wejściowy → wiele gorutyn
  • Fan-in: wiele kanałów → połączony kanał wyjściowy
  • Zawsze należy zamykać kanały po stronie wysyłającej; do anulowania należy używać kanału done
Bezpłatny start

Ucz się Go dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
51
Lekcje
203

Często zadawane pytania

Czy lekcja „Kierunek kanałów i wzorce potoków” jest bezpłatna?

Tak — pełny tekst „Kierunek kanałów i wzorce potoków” 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 „Kierunek kanałów i wzorce potoków”?

Kanały tylko do odczytu, tylko do zapisu i potoki Ć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 4 z 4.

Ile czasu zajmuje lekcja „Kierunek kanałów i wzorce potoków”?

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