Middle+
Porozprávajte o plánovaní goroutine v Go.
sobes.tech AI
Odpoveď od AI
Plánovač gorutín v jazyku Go používa model M:N, kde M gorutín je mapovaných na N vlákien OS. Efektívne prepína gorutiny medzi vláknami, implementujúc predčasné prerušovanie (preemption points) s bodmi zastavenia.
Hlavné komponenty modelu GPM:
- G (Goroutine): Abstrakcia vykonávanej jednotky kódu, ľahký tok, riadený plánovačom Go.
- P (Processor): Logický procesor, predstavujúci kontext lokálnej fronty gorutín a zdrojov, ako sú mutexy a plánovač. Počet P je štandardne určený počtom logických jadier procesora, ale môže byť zmenený cez
GOMAXPROCS. - M (Machine/Thread): Vlákno operačného systému, ktoré vykonáva kód. M je spojené s P a vykonáva gorutiny z lokálnej fronty P alebo globálnej fronty.
Princíp fungovania:
- Nová gorutina je vytvorená a umiestnená do lokálnej fronty dostupného P.
- M, spojené s P, vezme gorutinu z lokálnej fronty a vykoná ju.
- Ak je lokálna fronta prázdna, M sa pokúsi ukradnúť gorutiny od iných P.
- Ak P blokuje (napríklad pri systémovom volaní), spojenie s ním M odpojí a spojí sa s iným M. Zablokované M môže byť znovu použitý, keď systémové volanie skončí.
- Ak je gorutina zablokovaná na kanáli alebo mutexu, je zařadená do fronty čakania a plánovač vyberie inú gorutinu na vykonanie na tom istom M.
- Plánovač vykonáva periodické kontroly pre predčasné prerušovanie dlhodobo bežiacich gorutín, umožňujúc iným gorutinám získať čas na vykonanie.
Body predčasného prerušovania (preemption points) vznikajú na určitých miestach:
- Volania funkcií/metód.
- Slučky obsahujúce volania funkcií.
- Alokácia pamäte.
- Synchronizačné primitíva (kanály, mutexy).
Príklad plánovania:
package main
import (
"fmt"
"runtime"
"time"
)
func worker(id int) {
fmt.Printf("Gorutina %d začala prácu\n", id)
time.Sleep(time.Second) // Simulácia práce
fmt.Printf("Gorutina %d skončila prácu\n", id)
}
func main() {
runtime.GOMAXPROCS(2) // Nastavujeme počet logických procesorov
for i := 1; i <= 5; i++ {
go worker(i) // Vytvárame gorutiny
}
time.Sleep(3 * time.Second) // Čakáme na dokončenie gorutín
fmt.Println("Hlavná gorutina ukončená")
}
V tomto príklade je 5 gorutín naplánovaných na 2 logické procesory. Plánovač Go bude rozdeľovať tieto gorutiny medzi dostupné vlákna OS, využívajúc lokálne a prípadne globálne fronty, ako aj mechanizmus "krádeže" práce (work stealing).
Výhody takéhoto prístupu:
- Škálovateľnosť: Efektívne využitie viacjadrových systémov.
- Nízke režijné náklady: Ľahké gorutiny v porovnaní s vláknami OS.
- Jednoduchosť použitia: Integrované plánovanie v jazyku robí paralelné programovanie dostupnejším.