Direkt zum Hauptinhalt springen
eLearner.app
Modul 7 · Lektion 1 von 531/50 im Kurs~12 min
Lektionen des Moduls (1/5)

Goroutinen: leichtgewichtiger Parallelismus

Eine Goroutine ist eine Einheit zur gleichzeitigen Ausführung, die vom Go verwaltet wird Laufzeit. Sie starten eine mit dem Schlüsselwort go vor einer Funktion Anruf:

Go
go greet("Anna")         // chiamata a funzione esistente
go func() {              // funzione anonima
    fmt.Println("hi")
}()

Die go-Anweisung gibt sofort zurück: Die Goroutine beginnt in parallel und der Anrufer macht weiter.

Warum sie „leicht“ sind

  • Anfänglicher Stack ~2 KB (wächst dynamisch, bei Bedarf bis zu Hunderten von MB).
  • Multiplex auf eine kleine Anzahl von Betriebssystem-Threads durch die Laufzeit (GOMAXPROCS).
  • Tausende/Millionen Goroutinen sind Routine; Mit Betriebssystem-Threads wären sie unmöglich.

Vom Konzept her handelt es sich um „gleichzeitiges Feuern und Vergessen“, aber mit all dem Idiomatische Tools zur Koordinierung (Kanäle, sync.WaitGroup, context).

main wartet nicht

Go
func main() {
    go func() { fmt.Println("ciao dalla goroutine") }()
    // main esce subito: niente garanzia che la goroutine completi
}

Wenn main zurückkehrt, wird der Prozess beendet: Goroutinen werden nicht abgerufen Um „ihre Arbeit zu beenden“, werden sie einfach zusammen mit den getötet Prozess. Eine explizite Synchronisierung ist erforderlich.

Synchronisierung: eine Vorschau

Die drei Tools, die Sie in den nächsten Lektionen sehen werden:

Go
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
    work()
    done <- struct{}{}
}()
<-done   // attendi

// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()

// 3) context: cancellazione/timeout propagati

Der „klassische“ Closing-in-Loop-Bug

Bis Go 1.21 kam dies häufig vor:

Go
for i := 0; i < 3; i++ {
    go func() { fmt.Println(i) }()  // stampa "3 3 3" su versioni vecchie
}

Ab Go 1.22 wird die Variable for jeweils neu erstellt Iteration, sodass der Fehler standardmäßig behoben ist. In älteren Codebasen wird dies der Fall sein Ich sehe immer noch das Fixmuster:

Go
for i := 0; i < 3; i++ {
    i := i           // shadow esplicito
    go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
    go func(n int) { fmt.Println(n) }(i)
}

Goroutine != Betriebssystem-Thread

AspektGoroutineBetriebssystem-Thread
Anfangsstapel~2 KB1–8 MB (beim Start zugewiesen)
SchöpfungNanosekundenMikrosekunden
TerminplanungGehen Sie zur Laufzeit (kooperativ)Kernel (präventiv)
Praktische Zählung100K+ein paar tausend

Ein einzelner Betriebssystem-Thread kann viele Goroutinen hosten. Die Laufzeit verschiebt sich Goroutinen über Threads hinweg bei Bedarf.

Probieren Sie es aus

Übung#go.m7.l1.e1
Versuche: 0Wird geladen…

Starten Sie die Greet-Funktion als Goroutine und warten Sie 100 ms mit time.Sleep, bevor Sie main verlassen.

Editor wird geladen…
Hinweis anzeigen

`go` vor einem Funktionsaufruf startet die Goroutine.

Lösung nach 3 Versuchen verfügbar

Übung#go.m7.l1.e2
Versuche: 0Wird geladen…

Starten Sie eine anonyme Funktion als Goroutine, die „hi“ ausgibt.

Editor wird geladen…
Hinweis anzeigen

Eine anonyme Funktion wird mit einem abschließenden `()` aufgerufen: `go func(){...}()`.

Lösung nach 3 Versuchen verfügbar

Quiz#go.m7.l1.e3
Bereit

Was passiert, wenn `main` beendet wird, bevor eine Goroutine abgeschlossen ist?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
Antwortoptionen

Zusammenfassung

  • go f() startet gleichzeitig f(); es kehrt sofort zurück.
  • Goroutine = ~2 KB anfänglich, gemultiplext auf einige Betriebssystem-Threads.
  • main wartet nicht: Sie benötigen eine Synchronisierung (Kanal/WaitGroup/Kontext).
  • time.Sleep ist nur didaktisch, niemals für die Synchronisierung in der Produktion.
  • Ab Go 1.22 ist die for-Variable bei jeder Iteration „frisch“ (kein „3 3 3“-Fehler mehr).