Middle+
Papaskinkite apie goroutine planavimą Go kalboje.
sobes.tech AI
Atsakymas iš AI
Go kalbos gorutinų planuotojas naudoja M:N modelį, kur M gorutinų yra žemėlapis į N operacinės sistemos srautų. Jis efektyviai perjungia gorutinas tarp srautų, įgyvendindamas neprivestą daugiaveiksmingumą su stabdymo taškais (preemption points).
GPM modelio pagrindinės sudedamosios dalys:
- G (Goroutine): Kodo vykdymo vieneto abstrakcija, lengvas srautas, valdomas Go planuotojo.
- P (Processor): Loginis procesorius, atstovaujantis vietinio eilės ir išteklių, tokių kaip mutexai ir planuotojas, kontekstą. P skaičius pagal numatytuosius nustatymus nustatomas pagal loginių procesoriaus branduolių skaičių, tačiau gali būti pakeistas per
GOMAXPROCS. - M (Machine/Thread): Operacinės sistemos srautas, kuris vykdo kodą. M yra susijęs su P ir vykdo gorutinas iš vietinio P eilės arba globalios eilės.
Veikimo principas:
- Sukuriama nauja gorutina ir įdedama į prieinamą P vietinę eilę.
- P susijęs M paima gorutinas iš vietinės eilės ir vykdo ją.
- Jei vietinė eilė tuščia, M bando pavogti gorutinas iš kitų P.
- Jei P užstringa (pavyzdžiui, sistemos iškvietimo metu), susijęs M atjungiamas, ir jungiasi su kita M. Užstringęs M gali būti naudojamas pakartotinai, kai baigsis sistemos iškvietimas.
- Jei gorutina užstringa kanale ar mutex'e, ji įdedama į laukiančiųjų eilę, ir planuotojas pasirinks kitą gorutina tą patį M vykdyti.
- Planuotojas periodiškai tikrina, ar nereikėtų nutraukti ilgai trunkančias gorutinas, leidžiant kitoms gorutinoms gauti vykdymo laiką.
Preemption points (nutraukimo taškai) atsiranda tam tikrose vietose:
- Funkcijų/metodų iškvietimai.
- Ciklai, kuriuose yra funkcijų iškvietimų.
- Atminties paskirstymas.
- Sinchronizacijos primitivi (kanalai, mutexai).
Pavyzdys planavimas:
package main
import (
"fmt"
"runtime"
"time"
)
func worker(id int) {
fmt.Printf("Gorutina %d pradėjo darbą\n", id)
time.Sleep(time.Second) // Darbo imitacija
fmt.Printf("Gorutina %d baigė darbą\n", id)
}
func main() {
runtime.GOMAXPROCS(2) // Nustatome loginių procesorių skaičių
for i := 1; i <= 5; i++ {
go worker(i) // Gorutinų kūrimas
}
time.Sleep(3 * time.Second) // Laukiame gorutinų pabaigos
fmt.Println("Pagrindinė gorutina baigta")
}
Šiame pavyzdyje 5 gorutinos planuojamos 2 loginiuose procesoriuose. Go planuotojas paskirstys šias gorutinas tarp prieinamų OS srautų, naudodamas vietinius ir galbūt globalius eilės, taip pat "vagystės" mechanizmą.
Šio metodo privalumai:
- Mastelio didinimas: Efektyvus daugia branduolių sistemų išnaudojimas.
- Maži kaštai: Lengvos gorutinos, palyginti su OS srautais.
- Naudojimo paprastumas: Integruotas planavimas kalboje daro paralelinį programavimą prieinamesnį.