En Go, las variables globales se inicializan en el orden de declaración de los archivos y la secuencia de sus funciones init. Esto afecta la corrección del acceso al estado global: si una variable en un paquete depende del valor de una variable de otro paquete, pero ese paquete aún no ha sido inicializado, se producirá un error o un comportamiento no evidente.
No se puede establecer un "orden de inicio" directo entre las funciones init de diferentes paquetes. El compilador determina este orden según el gráfico de importaciones.
main.main().Ejemplo de inicialización diferida segura (lazy init):
var cfg *Config var once sync.Once func GetConfig() *Config { once.Do(func() { cfg = LoadConfigFromDisk() }) return cfg }
Pregunta: ¿Las funciones init de diferentes paquetes pueden ejecutarse al mismo tiempo (en paralelo) al iniciar un programa en Go?
Respuesta correcta: No, las funciones init y las variables globales se ejecutan estrictamente de manera secuencial en el orden que el compilador determina al analizar las dependencias entre los paquetes. No hay ejecución paralela de las funciones init.
Historia
En un gran proyecto, varios módulos cargaban configuraciones globales desde el disco a través de init(). Un módulo dependía de otro, pero debido a la reestructuración del gráfico go.mod, el orden de inicialización cambió — y de repente los valores de configuración resultaron vacíos. El error se manifestaba de manera aleatoria, dependiendo del orden de compilación, y solo se encontró después de analizar las dependencias y trasladar la inicialización a una función explícita.
Historia
En un servicio REST para el almacenamiento en caché de diccionarios se utilizaba un mapa global sin mutex. La inicialización comenzaba desde varios goroutines, que se iniciaban en init. Como resultado: carrera de datos, pánicos periódicos, datos no válidos dentro del mapa. Después de cambiar a sync.Once y realizar una llamada explícita a la inicialización, el problema desapareció.
Historia
Un logger global se creaba en la función init de uno de los paquetes auxiliares, sin embargo, otro paquete accedía a este logger también al inicio, antes de su inicialización. Como resultado, parte de los logs de inicio se perdía mientras el logger aún no existía. La solución: inyección de dependencias e inicialización explícita del logger antes de iniciar todos los servicios.