Sobes.tech
Middle

Was ist Laufzeit (Runtime) und wie funktioniert der Scheduler?

sobes.tech KI

Antwort von AI

Die Laufzeitumgebung (runtime) in Go ist die Umgebung, in der Go-Programme ausgeführt werden. Sie umfasst:

  • Speicherverwaltung: Der Garbage Collector verwaltet automatisch die Zuweisung und Freigabe von Speicher.
  • Scheduler: Verwalten die Ausführung von Goroutines auf Betriebssystem-Threads.
  • Systemaufrufe: Bieten eine Schnittstelle zur Interaktion mit dem Betriebssystem.
  • Synchronisationsprimitive: Kanäle, Mutexes und andere Mechanismen zur Koordination von Goroutines.

Der Go-Scheduler implementiert das M:N-Modell (viele Goroutines auf vielen Betriebssystem-Threads). Seine Hauptkomponenten:

  • G (Goroutine): Ein leichter Ausführungsthread, im Wesentlichen eine Abstraktion einer Funktion.
  • M (Machine): Ein Betriebssystem-Thread, auf dem Go-Programme ausgeführt werden.
  • P (Processor): Ein logischer Prozessor, der mit einem Betriebssystem-Thread verbunden ist. Er hält eine lokale runnable-Warteschlange für Goroutines.

Funktionsprinzip des Schedulers:

  1. Goroutines (G) werden erstellt und in eine globale oder lokale runnable-Warteschlange eingefügt.
  2. Betriebssystem-Threads (M) werden mit logischen Prozessoren (P) assoziiert. Die Anzahl der P entspricht standardmäßig der Anzahl der CPU-Kerne (GOMAXPROCS).
  3. P nimmt eine Goroutine aus seiner lokalen Warteschlange. Wenn die lokale Warteschlange leer ist, versucht P, Goroutines von einer anderen P oder der globalen Warteschlange "zu stehlen".
  4. M führt die Goroutine aus.
  5. Wenn eine Goroutine blockiert (z.B. beim Lesen aus einem Kanal ohne Daten oder bei einem Systemaufruf), trennt sich M von P, und P wird für die Ausführung einer anderen Goroutine verfügbar. Die blockierte Goroutine wird in Wartestellung versetzt. Wenn die Blockierung aufgehoben wird, wird die Goroutine wieder runnable und in die Warteschlange eingereiht.
  6. Wenn M bei einem Systemaufruf blockiert, hält es P nicht fest. Es kann ein neues M erstellt oder ein bestehendes verwendet werden, damit P andere Goroutines ausführen kann.

Dieser Ansatz ermöglicht eine effiziente Nutzung der Prozessorressourcen und eine einfache Skalierung paralleler Aufgaben, wobei die Komplexität der Betriebssystem-Thread-Verwaltung vor dem Entwickler verborgen bleibt.

// Beispiel für die Erstellung einer Goroutine
func myFunc() {
	// Goroutine-Code
}

func main() {
	go myFunc() // Start der Goroutine
	// Das Hauptprogramm läuft parallel weiter
}