Goroutines Grundlagen
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 59 von 107.
Eine Goroutine ist ein leichtgewichtiger Ausführungsthread, der von der Go-Runtime verwaltet wird. Im Gegensatz zu herkömmlichen Threads, die erheblichen Speicher verbrauchen, starten Goroutines mit nur wenigen Kilobyte Stack-Speicher, was es Ihnen ermöglicht, Tausende von ihnen gleichzeitig auszuführen.
Um eine Goroutine zu starten, setzen Sie einfach das Schlüsselwort go vor einen Funktionsaufruf:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // wird gleichzeitig ausgeführt
go sayHello("Bob") // wird gleichzeitig ausgeführt
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}Wenn go sayHello("Alice") ausgeführt wird, wartet es nicht darauf, dass sayHello beendet wird. Stattdessen fährt es sofort mit der nächsten Zeile fort, während sayHello im Hintergrund läuft. Deshalb benötigen wir time.Sleep – ohne dies würde main beendet werden, bevor die Goroutinen abgeschlossen sind.
Sie können Goroutinen auch mit anonymen Funktionen starten:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")Die main-Funktion selbst läuft in einer Goroutine. Wenn sie beendet wird, werden alle anderen Goroutinen sofort abgebrochen – sie erhalten keine Gelegenheit, ihre Ausführung abzuschließen. Die Verwendung von time.Sleep ist eine vorübergehende Lösung; in den kommenden Lektionen werden Sie fortgeschrittene Synchronisationstechniken mit Channels und WaitGroups kennenlernen.
Aufgabe
EinfachLassen Sie uns einen Task-Runner bauen, der mehrere Aufgaben gleichzeitig mit Goroutinen ausführt. Sie erstellen ein einfaches System, bei dem Aufgaben im Hintergrund laufen, während das Hauptprogramm fortgesetzt wird, was demonstriert, wie Goroutinen eine nebenläufige Ausführung ermöglichen.
Sie werden Ihren Code auf zwei Dateien verteilen:
tasks.go: Definieren Sie Ihre Logik zur Aufgabenausführung.Erstellen Sie ein
Task-Struct mit den FeldernName(string) undDuration(int, repräsentiert Millisekunden).Implementieren Sie eine Methode
Run()fürTask, die Arbeit simuliert, indem sie für die Dauer der Aufgabe pausiert und dann ausgibt:Task [Name] completedErstellen Sie eine Funktion
RunAllTasks(tasks []Task), die dieRun()-Methode jeder Aufgabe als Goroutine startet. Nach dem Starten aller Goroutinen sollte sie 500 Millisekunden lang pausieren, um deren Abschluss zu ermöglichen, und dann ausgeben:All tasks launchedmain.go: Lesen Sie die Aufgabeninformationen ein und orchestrieren Sie die nebenläufige Ausführung.Lesen Sie die Anzahl der Aufgaben ein, und lesen Sie dann für jede Aufgabe den Namen und die Dauer. Erstellen Sie die Aufgaben und übergeben Sie diese an
RunAllTasks. Nachdem die Funktion zurückkehrt, geben Sie aus:Main function done
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Anzahl der Aufgaben (Ganzzahl)
- Folgende Zeilen: Für jede Aufgabe zwei Zeilen - der Aufgabenname, dann die Dauer in Millisekunden
Zum Beispiel, gegeben:
3
Download
100
Process
50
Upload
75Ihre Ausgabe sollte den Abschluss der Aufgaben zeigen (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 Goroutinen gleichzeitig laufen, werden Aufgaben mit kürzerer Dauer normalerweise zuerst fertig. Die Reihenfolge des Aufgabenabschlusses hängt von deren Dauer ab - kürzere Aufgaben werden vor längeren abgeschlossen.
Verwenden Sie time.Sleep mit time.Duration und time.Millisecond für die Verzögerungen. Denken Sie daran, dass die Funktion ohne das Sleep in RunAllTasks zurückkehren würde, bevor die Goroutinen ihre Arbeit beendet haben.
Spickzettel
Eine Goroutine ist ein leichtgewichtiger Ausführungsthread, der von der Go-Runtime verwaltet wird. Goroutinen starten mit nur wenigen Kilobyte Stack-Speicher, was es ermöglicht, Tausende von ihnen gleichzeitig auszuführen.
Um eine Goroutine zu starten, setzen Sie das Schlüsselwort go vor einen Funktionsaufruf:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // wird gleichzeitig ausgeführt
go sayHello("Bob") // wird gleichzeitig ausgeführt
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}Wenn go sayHello("Alice") ausgeführt wird, wartet es nicht darauf, dass sayHello beendet wird. Stattdessen fährt es sofort mit der nächsten Zeile fort, während sayHello im Hintergrund läuft.
Sie können Goroutinen mit anonymen Funktionen starten:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")Die main-Funktion selbst läuft in einer Goroutine. Wenn sie zurückkehrt, werden alle anderen Goroutinen sofort beendet. Verwenden Sie time.Sleep, um Goroutinen den Abschluss zu ermöglichen, bevor die main-Funktion beendet wird.
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 den Namen und die Dauer jeder Aufgabe
// Für jede Aufgabe:
// - Lies den Aufgabennamen
// - Lies die Dauer (als Ganzzahl)
// - Erstelle eine Task und hänge sie an deinen Slice an
// TODO: Rufe RunAllTasks mit deinem Aufgaben-Slice auf
// TODO: Gib "Main function done" aus
}
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 Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool