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

  1. Nová gorutina je vytvorená a umiestnená do lokálnej fronty dostupného P.
  2. M, spojené s P, vezme gorutinu z lokálnej fronty a vykoná ju.
  3. Ak je lokálna fronta prázdna, M sa pokúsi ukradnúť gorutiny od iných P.
  4. 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čí.
  5. 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.
  6. 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.