ProgrammazioneSviluppatore Backend

Spiega cosa sono le goroutine in Go. Come funzionano internamente e in cosa si differenziano dai thread in altri linguaggi?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Una goroutine è un leggero thread di esecuzione gestito dal runtime di Go. Per avviare una nuova goroutine si usa la parola chiave go prima della chiamata alla funzione. Internamente, viene creata una struttura che descrive lo stack e lo stato del compito, che viene aggiunto nella coda dello scheduler delle goroutine.

A differenza dei thread del sistema operativo, una goroutine ha uno stack iniziale molto più piccolo (di solito 2 KB), e lo stack si espande automaticamente quando necessario. Lo scheduler di Go assegna automaticamente le goroutine ai thread disponibili del sistema operativo (modello M:N).

Le principali differenze rispetto ai thread:

  • Vengono creati più rapidamente e richiedono meno memoria
  • Vengono pianificati dal runtime di Go, e non dal sistema operativo
  • Si scalano automaticamente in base ai core
  • La sincronizzazione è realizzata tramite canali, il che previene molte classi di condizioni di gara

Esempio di utilizzo delle goroutine e dei canali:

package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d ha iniziato il lavoro %d\n", id, j) time.Sleep(time.Second) fmt.Printf("worker %d ha finito il lavoro %d\n", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 5) results := make(chan int, 5) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= 5; j++ { jobs <- j } close(jobs) for a := 1; a <= 5; a++ { <-results } }

Domanda trabocchetto

Le goroutine di Go possono essere eseguite in parallelo su più core?

Spesso si incontra una risposta errata: "No, perché Go utilizza thread verdi". In realtà, utilizzando una variabile d'ambiente o la chiamata runtime.GOMAXPROCS(n) Go può parallelizzare l'esecuzione delle goroutine su tutti i core disponibili del processore.

Esempio:

import "runtime" func main() { runtime.GOMAXPROCS(4) // Permette di utilizzare 4 core ... }

Esempi di errori reali a causa della scarsa comprensione del tema


Storia

In un progetto di servizio backend in Go è stato implementato un pool di worker tramite goroutine, ma i programmatori hanno dimenticato di limitare il numero di goroutine attive contemporaneamente. Di conseguenza, con carichi maggiori, l'applicazione avviava migliaia di goroutine, causando esaurimento della memoria e crash del servizio. Il problema è stato risolto introducendo un limite per le goroutine attive (ad esempio, tramite un semaphore o un worker pool).


Storia

Uno dei dipendenti ha sincronizzato erroneamente i dati tra le goroutine, utilizzando normali variabili globali senza mutex o canali. Questo ha causato una condizione di gara (race condition), provocando errori periodici durante l'elaborazione dei pagamenti. Il problema è stato scoperto solo dopo l'avvio in produzione.


Storia

Nel servizio di parsing è stata trascurata la gestione del passaggio di canali nil in select: dopo che il canale è stato chiuso, select continuava a bloccarsi in attesa di dati, e parte delle goroutine "si è bloccata". È stato risolto assegnando nil al canale chiuso e gestendo correttamente il select.