Powtórka – pula workerów
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 66 z 107.
Wyzwanie
ŁatwyZbudujmy system przetwarzania zadań z użyciem wzorca puli pracowników! Utworzysz pulę pracowników, którzy będą równolegle przetwarzać zadania obliczeniowe, pokazując, jak kanały, goroutines i WaitGroups współpracują, aby wydajnie obsługiwać obciążenie.
Rozmieścisz swój kod w dwóch plikach:
pool.go: Zdefiniuj komponenty puli pracowników i jej logikę.Utwórz strukturę
Taskz polamiID(int) iValue(int), które będą reprezentować pracę do wykonania.Utwórz strukturę
Resultz polamiTaskID(int) iComputed(int), które będą reprezentować wykonaną pracę.Zaimplementuj funkcję
Worker, która przyjmuje identyfikator pracownika (int), kanał zadań tylko do odbioru (<-chan Task), kanał wyników tylko do wysyłania (chan<- Result) oraz wskaźnik nasync.WaitGroup. Każdy pracownik powinien:- Użyć
defer wg.Done(), aby zasygnalizować zakończenie pracy - Iterować po kanale zadań, aby przetworzyć każde zadanie
- Dla każdego zadania obliczyć kwadrat wartości Value tego zadania
- Wysłać Result z TaskID i obliczonym kwadratem
Zaimplementuj funkcję
RunPool, która przyjmuje liczbę pracowników (int) oraz tablicę zadań. Funkcja powinna:- Utworzyć buforowane kanały zadań i wyników (użyć liczby zadań jako rozmiaru bufora)
- Uruchomić określoną liczbę pracowników jako goroutines
- Wysłać wszystkie zadania do kanału zadań, a następnie go zamknąć
- Użyć goroutine z WaitGroup do zamknięcia kanału wyników po zakończeniu pracy wszystkich pracowników
- Zebrać wszystkie wyniki do tablicy i ją zwrócić
- Użyć
main.go: Odczytaj dane wejściowe i koordynuj pulę pracowników.Odczytaj liczbę pracowników, a następnie liczbę zadań. Dla każdego zadania odczytaj jego identyfikator i wartość. Wywołaj
RunPool, przekazując pracowników i zadania, a następnie wypisz każdy wynik w formacie:Task [TaskID]: [Computed]Wypisz wyniki posortowane rosnąco według TaskID.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: Liczba pracowników (liczba całkowita)
- Wiersz 2: Liczba zadań (liczba całkowita)
- Kolejne wiersze: Dla każdego zadania dwa wiersze — identyfikator zadania (liczba całkowita), a następnie jego wartość (liczba całkowita)
Na przykład dla danych:
2
4
1
3
2
5
3
2
4
7Wynik powinien być następujący:
Task 1: 9
Task 2: 25
Task 3: 4
Task 4: 49Pracownicy przetwarzają zadania równolegle (kwadrat liczby 3 to 9, kwadrat liczby 5 to 25 itd.), a wyniki są zbierane i wyświetlane w kolejności według identyfikatorów zadań. Przy 2 pracownikach obsługujących 4 zadania praca jest wydajnie rozdzielana w puli.
Spróbuj swoich sił
package main
import (
"fmt"
"sort"
)
func main() {
// Wczytaj liczbę pracowników
var numWorkers int
fmt.Scanln(&numWorkers)
// Wczytaj liczbę zadań
var numTasks int
fmt.Scanln(&numTasks)
// Wczytaj zadania
tasks := make([]Task, numTasks)
for i := 0; i < numTasks; i++ {
var id, value int
fmt.Scanln(&id)
fmt.Scanln(&value)
tasks[i] = Task{ID: id, Value: value}
}
// TODO: Wywołaj RunPool z pracownikami i zadaniami
// TODO: Posortuj wyniki według TaskID w kolejności rosnącej
// TODO: Wypisz każdy wynik w formacie: Task [TaskID]: [Computed]
}
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online