Sobes.tech
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:

  1. Sukuriama nauja gorutina ir įdedama į prieinamą P vietinę eilę.
  2. P susijęs M paima gorutinas iš vietinės eilės ir vykdo ją.
  3. Jei vietinė eilė tuščia, M bando pavogti gorutinas iš kitų P.
  4. 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.
  5. Jei gorutina užstringa kanale ar mutex'e, ji įdedama į laukiančiųjų eilę, ir planuotojas pasirinks kitą gorutina tą patį M vykdyti.
  6. 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į.