Rückblick – Worker Pool
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 66 von 107.
Aufgabe
EinfachLassen Sie uns ein Aufgabenverarbeitungssystem nach dem Worker-Pool-Muster erstellen! Sie werden einen Pool von Workern erstellen, die Rechenaufgaben gleichzeitig verarbeiten, und so demonstrieren, wie Channels, Goroutinen und WaitGroups zusammenarbeiten, um Arbeitslasten effizient zu bewältigen.
Sie werden Ihren Code auf zwei Dateien verteilen:
pool.go: Definieren Sie Ihre Worker-Pool-Komponenten und die Logik.Erstellen Sie ein
Task-Struct mit den FeldernID(int) undValue(int), die die zu erledigende Arbeit repräsentieren.Erstellen Sie ein
Result-Struct mit den FeldernTaskID(int) undComputed(int), die die abgeschlossene Arbeit repräsentieren.Implementieren Sie eine
Worker-Funktion, die eine Worker-ID (int), einen Empfangskanal für Aufgaben (<-chan Task), einen Sendekanal für Ergebnisse (chan<- Result) und einen Zeiger auf einesync.WaitGroupentgegennimmt. Jeder Worker sollte:defer wg.Done()verwenden, um den Abschluss zu signalisieren- Über den Aufgaben-Channel iterieren (range), um jede Aufgabe zu verarbeiten
- Für jede Aufgabe das Quadrat des
Valueder Aufgabe berechnen - Ein
Resultmit derTaskIDund dem berechneten Quadrat senden
Implementieren Sie eine
RunPool-Funktion, die die Anzahl der Worker (int) und eine Slice von Aufgaben entgegennimmt. Diese Funktion sollte:- Gepufferte Channels für Aufgaben und Ergebnisse erstellen (verwenden Sie die Anzahl der Aufgaben als Puffergröße)
- Die angegebene Anzahl von Workern als Goroutinen starten
- Alle Aufgaben an den Aufgaben-Channel senden und diesen dann schließen
- Eine Goroutine mit der
WaitGroupverwenden, um den Ergebnis-Channel zu schließen, nachdem alle Worker fertig sind - Alle Ergebnisse in einer Slice sammeln und diese zurückgeben
main.go: Eingaben lesen und den Worker-Pool koordinieren.Lesen Sie die Anzahl der Worker und dann die Anzahl der Aufgaben ein. Lesen Sie für jede Aufgabe deren ID und Wert ein. Rufen Sie
RunPoolmit den Workern und Aufgaben auf und geben Sie dann jedes Ergebnis im FormatTask [TaskID]: [Computed]aus.Geben Sie die Ergebnisse nach
TaskIDaufsteigend sortiert aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Anzahl der Worker (Ganzzahl)
- Zeile 2: Anzahl der Aufgaben (Ganzzahl)
- Folgende Zeilen: Für jede Aufgabe zwei Zeilen - die Aufgaben-ID (Ganzzahl), dann ihr Wert (Ganzzahl)
Zum Beispiel bei folgender Eingabe:
2
4
1
3
2
5
3
2
4
7Sollte Ihre Ausgabe wie folgt aussehen:
Task 1: 9
Task 2: 25
Task 3: 4
Task 4: 49Die Worker verarbeiten Aufgaben gleichzeitig (3 zum Quadrat ist 9, 5 zum Quadrat ist 25 usw.), und die Ergebnisse werden gesammelt und nach Aufgaben-ID sortiert angezeigt. Mit 2 Workern, die 4 Aufgaben bearbeiten, wird die Arbeit effizient über den Pool verteilt.
Probier es selbst
package main
import (
"fmt"
"sort"
)
func main() {
// Anzahl der Arbeiter lesen
var numWorkers int
fmt.Scanln(&numWorkers)
// Anzahl der Aufgaben lesen
var numTasks int
fmt.Scanln(&numTasks)
// Aufgaben lesen
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: RunPool mit Arbeitern und Aufgaben aufrufen
// TODO: Ergebnisse nach TaskID in aufsteigender Reihenfolge sortieren
// TODO: Jedes Ergebnis im Format ausgeben: Task [TaskID]: [Computed]
}
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler