Sobes.tech
Middle+

Povězte o plánování goroutines v Go.

sobes.tech AI

Odpověď od AI

Plánovač gorutin v jazyce Go používá model M:N, kde M gorutin je mapováno na N vlákna OS. Efektivně přepíná gorutiny mezi vlákny, implementujíc předčasné přerušení (preemption points) s body zastavení.

Hlavní komponenty modelu GPM:

  • G (Goroutine): Abstrakce vykonávající jednotky kódu, lehký vlákno řízené plánovačem Go.
  • P (Processor): Logický procesor, představující kontext lokální fronty gorutin a zdrojů, jako jsou mutexy a plánovač. Počet P je standardně určen počtem logických jader procesoru, ale může být změněn přes GOMAXPROCS.
  • M (Machine/Thread): Vlákno operačního systému, které vykonává kód. M je spojeno s P a vykonává gorutiny z lokální fronty P nebo globální fronty.

Princip fungování:

  1. Nová gorutina je vytvořena a umístěna do lokální fronty dostupného P.
  2. M, spojené s P, vezme gorutinu z lokální fronty a vykoná ji.
  3. Pokud je lokální fronta prázdná, M se pokusí ukrást gorutiny od jiných P.
  4. Pokud je P zablokováno (například při volání systémových funkcí), spojení s ním M odpojí a spojí se s jiným M. Zablokované M může být znovu použito, když systémové volání skončí.
  5. Pokud je gorutina zablokována na kanálu nebo mutexu, je zařazena do fronty čekání a plánovač vybere jinou gorutinu k vykonání na stejném M.
  6. Plánovač provádí periodické kontroly pro předčasné přerušení dlouho běžících gorutin, umožňující jiným gorutinám získat čas na vykonání.

Body předčasného přerušení (preemption points) vznikají na určitých místech:

  • Volání funkcí/metod.
  • Smyčky obsahující volání funkcí.
  • Alokace paměti.
  • Synchronizační primitivy (kanály, mutexy).

Příklad plánování:

package main

import (
	"fmt"
	"runtime"
	"time"
)

func worker(id int) {
	fmt.Printf("Gorutina %d začala práci\n", id)
	time.Sleep(time.Second) // Simulace práce
	fmt.Printf("Gorutina %d skončila práci\n", id)
}

func main() {
	runtime.GOMAXPROCS(2) // Nastavení počtu logických procesorů

	for i := 1; i <= 5; i++ {
		go worker(i) // Vytvoření gorutin
	}

	time.Sleep(3 * time.Second) // Čekání na dokončení gorutin
	fmt.Println("Hlavní gorutina ukončena")
}

V tomto příkladu jsou 5 gorutin naplánováno na 2 logické procesory. Plánovač Go bude rozdělovat tyto gorutiny mezi dostupné vlákna OS, využívajíc lokální a případně globální fronty, stejně jako mechanismus "krádeže" práce (work stealing).

Výhody tohoto přístupu:

  • Škálovatelnost: Efektivní využití vícejádrových systémů.
  • Nízké režijní náklady: Lehká gorutina ve srovnání s vlákny OS.
  • Jednoduchost použití: Integrované plánování v jazyce činí paralelní programování přístupnější.