Menu
Coddy logo textTech

Kanały buforowane i niebuforowane

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

Kanały, których używaliśmy do tej pory, są niebuforowane. Nie mają pojemności pozwalającej przechowywać wartości. Operacja wysyłania blokuje się, dopóki inna gorutyna nie odbierze wartości, i odwrotnie. Kanały buforowane dodają wewnętrzną pamięć, dzięki czemu wysyłanie może zakończyć się bez natychmiastowego odbiorcy.

Utwórz buforowany kanał, przekazując pojemność do make:

// Niebuforowany - pojemność 0
unbuffered := make(chan int)

// Buforowany - pojemność 3
buffered := make(chan int, 3)

W przypadku kanału buforowanego wysyłanie blokuje się tylko wtedy, gdy bufor jest pełny, a odbieranie — tylko wtedy, gdy bufor jest pusty:

ch := make(chan string, 2)

ch <- "first"   // nie blokuje - w buforze jest miejsce
ch <- "second"  // nie blokuje - w buforze jest miejsce
// ch <- "third" zablokowałoby się - bufor jest pełny

fmt.Println(<-ch)  // "first"
fmt.Println(<-ch)  // "second"

Użyj len(ch), aby sprawdzić, ile elementów znajduje się obecnie w buforze, a cap(ch), aby sprawdzić jego całkowitą pojemność.

Kiedy używać poszczególnych rodzajów: Kanały niebuforowane zapewniają ścisłą synchronizację: nadawca wie, że odbiorca otrzymał wartość. Kanały buforowane uniezależniają czas działania nadawcy i odbiorcy, co jest przydatne, gdy producenci i konsumenci pracują z różną szybkością. Kanały buforowane mogą jednak maskować błędy synchronizacji, dlatego preferuj kanały niebuforowane, chyba że masz konkretny powód, by użyć buforowania.

challenge icon

Wyzwanie

Łatwy

Zbudujemy procesor wsadowy, który pokazuje różnicę między kanałami buforowanymi i niebuforowanymi. Utworzysz system, w którym producent wysyła elementy do procesora, używając kanałów buforowanych, aby oddzielić ich tempo pracy i umożliwić producentowi pracę z wyprzedzeniem.

Rozmieścisz kod w dwóch plikach:

  • processor.go: Zdefiniuj logikę przetwarzania wsadowego za pomocą kanałów.

    Utwórz strukturę Item z polami ID (int) i Value (string).

    Zaimplementuj dwie funkcje:

    • Producer(items []Item, out chan Item) - Wysyła każdy element do kanału wyjściowego. Po wysłaniu każdego elementu wypisz: Produced item [ID]. Po wysłaniu wszystkich elementów wypisz bieżącą liczbę elementów oczekujących w buforze, używając len(out) w formacie: Buffer has [count] items. Następnie zamknij kanał.
    • Consumer(in chan Item) []string - Odbiera elementy z kanału wejściowego i zbiera je w tablicy sformatowanych ciągów znaków. Dla każdego odebranego elementu ciąg powinien mieć postać: Consumed: [ID] - [Value]. Zwróć tablicę po zamknięciu kanału.
  • main.go: Skonfiguruj kanał buforowany oraz skoordynuj działanie producenta i konsumenta.

    Odczytaj pojemność bufora, następnie liczbę elementów, a potem identyfikator i wartość każdego elementu. Utwórz kanał buforowany o określonej pojemności. Najpierw uruchom Producer (nie jako gorutynę), aby wypełnić bufor, a następnie uruchom Consumer, aby przetworzyć wszystkie elementy. Wypisz każdy przetworzony wynik w osobnym wierszu.

Dane wejściowe będą następujące:

  • Wiersz 1: Pojemność bufora (liczba całkowita)
  • Wiersz 2: Liczba elementów (liczba całkowita)
  • Kolejne wiersze: Dla każdego elementu dwa wiersze — identyfikator elementu (liczba całkowita), a następnie jego wartość (ciąg znaków)

Na przykład, dla danych:

3
3
1
apple
2
banana
3
cherry

Oczekiwany wynik:

Produced item 1
Produced item 2
Produced item 3
Buffer has 3 items
Consumed: 1 - apple
Consumed: 2 - banana
Consumed: 3 - cherry

Zauważ, że przy pojemności bufora równej 3 producent może wysłać wszystkie 3 elementy bez blokowania, a przed rozpoczęciem pracy konsumenta w buforze widać 3 oczekujące elementy. Jeśli bufor byłby mniejszy niż liczba elementów, producent zablokowałby się, czekając na wolne miejsce.

Spróbuj swoich sił

package main

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

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

	// Odczytaj pojemność bufora
	line, _ := reader.ReadString('\n')
	bufferCapacity, _ := strconv.Atoi(strings.TrimSpace(line))

	// Odczytaj liczbę elementów
	line, _ = reader.ReadString('\n')
	numItems, _ := strconv.Atoi(strings.TrimSpace(line))

	// Odczytaj ID i wartość każdego elementu
	items := make([]Item, numItems)
	for i := 0; i < numItems; i++ {
		line, _ = reader.ReadString('\n')
		id, _ := strconv.Atoi(strings.TrimSpace(line))
		line, _ = reader.ReadString('\n')
		value := strings.TrimSpace(line)
		items[i] = Item{ID: id, Value: value}
	}

	// TODO: Utwórz kanał buforowany o określonej pojemności

	// TODO: Uruchom Producer (nie jako goroutine), aby wypełnić bufor

	// TODO: Uruchom Consumer, aby przetworzyć wszystkie elementy

	// TODO: Wypisz każdy wynik pobrany z kanału 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