In Go worden globale variabelen geïnitieerd volgens de volgorde van bestandverklaringen en de volgorde van hun init-functies. Dit bepaalt de correctheid van de toegang tot de globale toestand: als een variabele in het ene pakket afhankelijk is van de waarde van een variabele in een ander pakket, maar dat pakket is nog niet geïnitialiseerd — zal er een fout of onvoorspelbaar gedrag optreden.
Er kan geen directe "startvolgorde" worden ingesteld tussen init-functies van verschillende pakketten. De compiler bepaalt deze volgorde op basis van de importgrafiek.
main.main() wordt aangeroepen.Voorbeeld van veilige uitgestelde initialisatie (lazy init):
var cfg *Config var once sync.Once func GetConfig() *Config { once.Do(func() { cfg = LoadConfigFromDisk() }) return cfg }
Vraag: Kunnen init-functies van verschillende pakketten tegelijkertijd (parallel) worden uitgevoerd bij het starten van een Go-programma?
Correct antwoord: Nee, init-functies en globale variabelen worden strikt sequentieel uitgevoerd in de volgorde die door de compiler is bepaald bij het analyseren van de afhankelijkheden tussen pakketten. Parallelle uitvoering van init-functies vindt niet plaats.
Verhaal
In een groot project laadden verschillende modules globale configuraties vanaf de schijf via init(). Eén module was afhankelijk van een andere, maar door de herstructurering van de go.mod-grafiek was de initialisatievolgorde veranderd — en plotseling bleken de waarden van de configuraties leeg te zijn! De fout kwam willekeurig voor, afhankelijk van de volgorde van de compilatie, en werd pas gevonden na het analyseren van de afhankelijkheden en het omzetten van de initialisatie naar een expliciete functie.
Verhaal
In een REST-service werd een globale map zonder mutex gebruikt voor het cachen van referentiedatabases. De initialisatie begon vanuit verschillende goroutines die in init werden gestart. Het resultaat — data race, periodieke paniek, ongeldige gegevens binnen de map. Na vervanging door sync.Once en expliciete aanroep van de initialisatie verdween het probleem.
Verhaal
Een globale logger werd gecreëerd in de init-functie van een van de hulppakketten, maar een ander pakket benaderde deze logger ook bij de opstart, voordat deze was geïnitialiseerd. Het resultaat was dat een deel van de opstartlogs verloren ging, terwijl de logger nog niet bestond. De oplossing — afhankelijkheidsinjectie en expliciete initialisatie van de logger vóór het starten van alle services.