Kierunek kanałów i wzorce potoków
Kanały tylko do odczytu, tylko do zapisu i potoki
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 dochan<-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<- Ttylko do wysyłania,<-chan Ttylko 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
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
- Uruchamianie goroutines
- Kanały niebuforowane
- Kanały buforowane
- Kierunek kanałów i wzorce potoków