Sobes.tech
Middle

Cos'è il tempo di esecuzione (runtime) e come funziona il pianificatore (scheduler)?

sobes.tech AI

Risposta dell'AI

Il runtime in Go è l'ambiente in cui vengono eseguiti i programmi Go. Include:

  • Gestione della memoria: Il garbage collector gestisce automaticamente l'allocazione e la liberazione della memoria.
  • Scheduler: Gestisce l'esecuzione delle goroutine sui thread del sistema operativo.
  • Chiamate di sistema: Fornisce un'interfaccia per interagire con il sistema operativo.
  • Primitive di sincronizzazione: Canali, mutex e altri meccanismi per coordinare le goroutine.

Il scheduler di Go implementa il modello M:N (molte goroutine su molti thread del sistema operativo). I suoi componenti principali:

  • G (Goroutine): Un thread leggero di esecuzione, fondamentalmente un'astrazione di una funzione.
  • M (Machine): Un thread del sistema operativo su cui vengono eseguiti i programmi Go.
  • P (Processor): Un processore logico associato a un thread del sistema operativo. Tiene una coda locale runnable di goroutine.

Principio di funzionamento dello scheduler:

  1. Le goroutine (G) vengono create e aggiunte a una coda globale o locale runnable.
  2. I thread del sistema operativo (M) sono associati a processori logici (P). La quantità di P di default è uguale al numero di core CPU (GOMAXPROCS).
  3. P prende una goroutine dalla sua coda locale. Se la coda locale è vuota, P tenta di "rubare" (steal) goroutine dalla coda di un altro P o dalla coda globale.
  4. M esegue la goroutine.
  5. Quando una goroutine si blocca (ad esempio, durante la lettura da un canale senza dati o durante una chiamata di sistema), M si disassocia da P, e P diventa disponibile per eseguire un'altra goroutine. La goroutine bloccata viene messa in attesa. Quando il blocco viene rimosso, la goroutine torna a essere runnable e viene rimessa in coda.
  6. Se M si blocca in una chiamata di sistema, non mantiene P. Può essere creato un nuovo M o può essere usato uno esistente, affinché P possa continuare a eseguire altre goroutine.

Questo approccio permette di utilizzare efficacemente le risorse del processore e di scalare facilmente le attività parallele, nascondendo la complessità della gestione dei thread del sistema operativo allo sviluppatore.

// Esempio di creazione di una goroutine
func myFunc() {
	// Codice della goroutine
}

func main() {
	go myFunc() // Avvio della goroutine
	// Il programma principale continua l'esecuzione in parallelo
}