Grundlagen der Goroutines
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 59 von 107.
Eine Goroutine ist ein leichtgewichtiger Ausführungs-Thread, der von der Go-Laufzeitumgebung verwaltet wird. Im Gegensatz zu herkömmlichen Threads, die erheblichen Speicher verbrauchen, starten Goroutines mit nur wenigen Kilobyte Stack-Speicher, sodass du Tausende davon gleichzeitig ausführen kannst.
Um eine Goroutine zu starten, setze einfach das Schlüsselwort go vor einen Funktionsaufruf:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // läuft gleichzeitig
go sayHello("Bob") // läuft gleichzeitig
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}Wenn go sayHello("Alice") ausgeführt wird, wartet es nicht darauf, dass sayHello fertig wird. Stattdessen fährt es sofort mit der nächsten Zeile fort, während sayHello im Hintergrund ausgeführt wird. Deshalb benötigen wir time.Sleep; ohne dieses würde main beendet werden, bevor die Goroutinen abgeschlossen sind.
Du kannst Goroutines auch mit anonymen Funktionen starten:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")Die Hauptfunktion selbst wird in einer Goroutine ausgeführt. Wenn sie zurückkehrt, werden alle anderen Goroutines sofort beendet. Sie bekommen keine Gelegenheit, fertig zu werden. Die Verwendung von time.Sleep ist eine vorübergehende Lösung; in den kommenden Lektionen lernst du richtige Synchronisierungstechniken mit Channels und Wait Groups kennen.
Aufgabe
EinfachErstellen wir einen Task-Runner, der mehrere Tasks gleichzeitig mithilfe von Goroutines ausführt. Du erstellst ein einfaches System, in dem Tasks im Hintergrund ausgeführt werden, während das Hauptprogramm fortfährt, und demonstrierst damit, wie Goroutines eine nebenläufige Ausführung ermöglichen.
Du wirst deinen Code auf zwei Dateien verteilen:
tasks.go: Definiere hier die Logik zur Task-Ausführung.Erstelle eine
Task-Struktur mit den FeldernName(string) undDuration(int, repräsentiert Millisekunden).Implementiere eine Methode
Run()fürTask, die Arbeit simuliert, indem sie für die Dauer des Tasks wartet und anschließend Folgendes ausgibt:Task [Name] completedErstelle eine Funktion
RunAllTasks(tasks []Task), die die MethodeRun()jedes Tasks als Goroutine startet. Nachdem alle Goroutines gestartet wurden, soll sie 500 Millisekunden warten, damit diese abgeschlossen werden können, und anschließend Folgendes ausgeben:All tasks launchedmain.go: Lies die Task-Informationen ein und koordiniere die nebenläufige Ausführung.Lies die Anzahl der Tasks ein und anschließend den Namen und die Dauer jedes Tasks. Erstelle die Tasks und übergib sie an
RunAllTasks. Gib nach der Rückkehr Folgendes aus:Main function done
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Anzahl der Tasks (Ganzzahl)
- Folgende Zeilen: Für jeden Task zwei Zeilen – zuerst der Task-Name, dann seine Dauer in Millisekunden
Zum Beispiel bei folgender Eingabe:
3
Download
100
Process
50
Upload
75Deine Ausgabe sollte die abgeschlossenen Tasks anzeigen (die Reihenfolge kann aufgrund der Nebenläufigkeit variieren), gefolgt von den Abschlussmeldungen:
Task Process completed
Task Upload completed
Task Download completed
All tasks launched
Main function doneHinweis: Da Goroutines nebenläufig ausgeführt werden, werden Tasks mit kürzeren Dauern normalerweise zuerst abgeschlossen. Die Reihenfolge, in der die Tasks abgeschlossen werden, hängt von ihren Dauern ab – kürzere Tasks werden vor längeren abgeschlossen.
Verwende time.Sleep zusammen mit time.Duration und time.Millisecond für die Verzögerungen. Denke daran, dass die Funktion ohne die Wartezeit in RunAllTasks zurückkehren würde, bevor die Goroutines ihre Arbeit beendet haben.
Probier es selbst
package main
import (
"fmt"
)
func main() {
// Lies die Anzahl der Aufgaben
var numTasks int
fmt.Scanln(&numTasks)
// TODO: Erstelle einen Slice, um die Aufgaben zu speichern
// TODO: Lies Name und Dauer jeder Aufgabe
// Für jede Aufgabe:
// - Lies den Aufgabennamen
// - Lies die Dauer (als Ganzzahl)
// - Erstelle eine Task und füge sie deinem Slice hinzu
// TODO: Rufe RunAllTasks mit deinem tasks-Slice auf
// TODO: Print "Main function done"
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
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