Der größte Anfängerfehler in Go ist es, einfache Concurrency mit kostenloser Concurrency zu verwechseln.
Goroutinen sind leichtgewichtig, was ausgezeichnet ist. Sie stellen jedoch immer noch Arbeit dar und verbrauchen CPU, Speicher, Sockets und Dateideskriptoren indirekt durch die Aufgaben, die sie ausführen.
Das Problem mit unbegrenztem Fanout
Das ist in kleinem Maßstab in Ordnung:
for _, file := range files {
go downloadAndParse(file)
}
Es wird gefährlich, wenn die Schleifenhöhe externe Eingaben oder einen großen Rückstand widerspiegelt.
Der übliche Fehler ist nicht "zu viele Goroutinen" als abstrakte Zahl. Es ist die Erschöpfung im Nachlauf:
- zu viele offene Verbindungen
- zu hoher I/O-Druck
- zu viel Speicher in Benutzung
Warum Arbeiterpools hilfreich sind
Arbeiterpools erzeugen Rückdruck, indem sie beschränken, wie viel Arbeit gleichzeitig ausgeführt werden kann:
func worker(jobs <-chan string, results chan<- error) {
for file := range jobs {
results <- downloadAndParse(file)
}
}
Der Punkt ist Kontrolle, nicht Stil.
Sie entscheiden, wie viel Arbeit das System gleichzeitig zulassen sollte, basierend auf dem tatsächlichen Engpass.
Kanäle dienen der Koordination
Kanäle sind nützlich, weil sie die Übergabe ausdrücklich machen:
- wer die Arbeit produziert
- wer sie konsumiert
- wo das System blockiert werden sollte
Das ist oft klarer und sicherer, als das Fanout der Concurrency implizit zuzulassen.
Die bessere Regel
Fragen Sie nicht "Kann das concurrent sein?"
Fragen Sie:
- Was ist der Engpass?
- Wie viel Concurrency kann es tolerieren?
- Wo sollte Rückdruck stattfinden?
So wird Go-Concurrency zu Ingenieurkunst statt zu Optimismus.
Weitere Lektüre