almessadi.
Zur Übersicht

Go-Concurrency benötigt Grenzen, nicht nur Goroutinen_

Goroutinen sind günstig, aber nicht umsonst. Arbeiterpools und Kanäle sind wichtig, da Concurrency ohne Rückdruck eine weitere Möglichkeit darstellt, ein System zu überlasten.

Veröffentlicht18. Juni 2024
Lesezeit6 min read

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