In Go, le variabili globali vengono inizializzate nell'ordine di dichiarazione dei file e della sequenza delle loro funzioni init. Ciò influisce sulla correttezza dell'accesso allo stato globale: se una variabile in un pacchetto dipende dal valore di una variabile di un altro pacchetto, ma quel pacchetto non è ancora stato inizializzato, si verificherà un errore o un comportamento imprevisto.
Non è possibile impostare direttamente un "ordine di avvio" tra le funzioni init di diversi pacchetti. Il compilatore determina questo ordine in base al grafo degli import.
main.main().Esempio di inizializzazione ritardata sicura (lazy init):
var cfg *Config var once sync.Once func GetConfig() *Config { once.Do(func() { cfg = LoadConfigFromDisk() }) return cfg }
Domanda: Possono le funzioni init di pacchetti diversi essere eseguite contemporaneamente (in parallelo) all'avvio del programma in Go?
Risposta corretta: No, le funzioni init e le variabili globali vengono eseguite rigorosamente in sequenza secondo l'ordine determinato dal compilatore durante l'analisi delle dipendenze tra pacchetti. Non si verifica un'avvio parallelo delle funzioni init.
Storia
In un grande progetto, diversi moduli caricavano configurazioni globali dal disco tramite init(). Un modulo dipendeva da un altro, ma a causa della ristrutturazione del grafo go.mod, l'ordine di inizializzazione è cambiato — e improvvisamente i valori della configurazione erano vuoti! L'errore si manifestava in modo casuale, a seconda dell'ordine di build, e fu scoperto solo dopo aver analizzato le dipendenze e trasferito l'inizializzazione in una funzione esplicita.
Storia
In un servizio REST per la memorizzazione nella cache di dizionari, è stata utilizzata una mappa globale senza mutex. L'inizializzazione iniziava da più goroutine, avviate in init. Come risultato — data race, panico periodico, dati non validi all'interno della mappa. Dopo aver sostituito con sync.Once e una chiamata esplicita all'inizializzazione, il problema è scomparso.
Storia
Il logger globale veniva creato nella funzione init di uno dei pacchetti ausiliari, ma un altro pacchetto accedeva a questo logger anche all'avvio, prima della sua inizializzazione. Di conseguenza, parte dei log di avvio veniva persa, poiché il logger non esisteva ancora. La soluzione — iniezione di dipendenze e inizializzazione esplicita del logger prima dell'avvio di tutti i servizi.