Menu
Coddy logo textTech

Kanały i komunikacja

Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 60 z 107.

Kanały to podstawowy mechanizm komunikacji między goroutines w Go. Zamiast bezpośrednio współdzielić pamięć, goroutines wysyłają i odbierają wartości za pośrednictwem kanałów, zgodnie z filozofią Go: „Nie komunikuj się przez współdzielenie pamięci; współdziel pamięć, komunikując się”.

Utwórz kanał za pomocą funkcji make i słowa kluczowego chan:

messages := make(chan string)

Użyj operatora strzałki <-, aby wysyłać i odbierać wartości. Strzałka wskazuje kierunek przepływu danych:

func main() {
    messages := make(chan string)
    
    go func() {
        messages <- "hello"  // wyślij do kanału
    }()
    
    msg := <-messages  // odbierz z kanału
    fmt.Println(msg)   // Wynik: hello
}

Domyślnie wysyłanie i odbieranie blokuje, dopóki druga strona nie będzie gotowa. Gdy gorutyna wysyła "hello", czeka, aż main będzie gotowe do odbioru. To blokujące zachowanie zapewnia wbudowaną synchronizację: time.Sleep nie jest potrzebne.

Możesz zamknąć kanał, aby zasygnalizować, że nie zostaną wysłane już żadne wartości. Odbiorcy mogą to wykryć:

close(messages)

msg, ok := <-messages
if !ok {
    fmt.Println("Channel closed")
}

Kanały mają określony typ: chan string może przenosić tylko ciągi znaków. Bezpieczeństwo typów gwarantuje, że gorutyny komunikują się przy użyciu właściwych typów danych, wykrywając błędy na etapie kompilacji, a nie w czasie wykonywania.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system przekazywania wiadomości, który pokazuje, jak goroutines komunikują się za pośrednictwem kanałów. Utworzysz potok, w którym wiadomości przepływają między różnymi etapami przetwarzania, a każdy etap działa współbieżnie.

Rozmieścisz swój kod w dwóch plikach:

  • relay.go: Zdefiniuj potok przetwarzania wiadomości.

    Utwórz strukturę Message z polami ID (int) i Content (string).

    Zaimplementuj trzy funkcje reprezentujące różne etapy potoku:

    • Producer(messages []Message, out chan Message) - Przyjmuje wycinek wiadomości i wysyła każdą z nich do kanału wyjściowego. Po wysłaniu wszystkich wiadomości zamknij kanał, aby zasygnalizować zakończenie.
    • Transformer(in chan Message, out chan Message) - Odbiera wiadomości z kanału wejściowego, zamienia treść każdej wiadomości na wielkie litery i wysyła przekształconą wiadomość do kanału wyjściowego. Gdy kanał wejściowy zostanie zamknięty (co wykrywa się za pomocą formy odbioru z dwoma wartościami), zamknij kanał wyjściowy.
    • Consumer(in chan Message) []string - Odbiera wszystkie wiadomości z kanału wejściowego i zbiera je w wycinku sformatowanych ciągów znaków. Każdy ciąg znaków powinien mieć format: Message [ID]: [Content]. Zwróć wycinek po zamknięciu kanału.
  • main.go: Skonfiguruj potok i zarządzaj współbieżnym przepływem wiadomości.

    Odczytaj liczbę wiadomości, a następnie identyfikator i treść każdej wiadomości. Utwórz wiadomości i skonfiguruj dwa kanały łączące trzy etapy potoku. Uruchom Producer i Transformer jako goroutines, a następnie uruchom Consumer w głównej goroutine, aby zebrać wyniki. Wypisz każdy wynik w osobnym wierszu.

Podane zostaną następujące dane wejściowe:

  • Wiersz 1: Liczba wiadomości (liczba całkowita)
  • Kolejne wiersze: Dla każdej wiadomości dwa wiersze — identyfikator wiadomości (liczba całkowita), a następnie jej treść (ciąg znaków)

Na przykład, dla danych:

2
1
hello world
2
go channels

Wynik powinien być następujący:

Message 1: HELLO WORLD
Message 2: GO CHANNELS

Potok działa tak: Producer wysyła wiadomości → Transformer zamienia je na wielkie litery → Consumer zbiera i formatuje wyniki. Każda strzałka reprezentuje kanał łączący współbieżne etapy. Do zamiany tekstu na wielkie litery użyj pakietu strings.

Spróbuj swoich sił

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Odczytaj liczbę wiadomości
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// Odczytaj wiadomości
	messages := make([]Message, n)
	for i := 0; i < n; i++ {
		idLine, _ := reader.ReadString('\n')
		id, _ := strconv.Atoi(strings.TrimSpace(idLine))
		contentLine, _ := reader.ReadString('\n')
		content := strings.TrimSpace(contentLine)
		messages[i] = Message{ID: id, Content: content}
	}

	// TODO: Utwórz dwa kanały łączące etapy potoku

	// TODO: Uruchom Producer jako gorutynę

	// TODO: Uruchom Transformer jako gorutynę

	// TODO: Uruchom Consumer w głównej gorutynie i zbierz wyniki

	// TODO: Wypisz każdy wynik w osobnym wierszu
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Go online