[备忘] Go init 行为
本文首发于 xlog,可跳转获得更佳阅读体验
总结
基础规则:
- 所有的 init 函数都在一个 Goroutine 中执行(但请参见下面的特殊注意)
- 如果 package a 引用了 package b,那么 a 的 init 一定在 b 的 init 运行完成后运行
- main package 的 main 函数一定在其他 init 函数均运行完成后再运行(即运行顺序为 package 的 init -> main 的 init -> main 的 main)
- 同一 package 中的多个文件中的 init 执行顺序未定义,同一文件中的 init 自上而下运行
- 如果 package a 同时引用了 package b 和 c,那么 b 与 c 的 init 顺序在 Go1.21 及之后定义
在 Go1.20 及之前:
- 如果 package a 引用了 package b,那么 b 的 init 一定在 a 之前运行
- 但是,如果 package a 同时引用了 package b 和 c,只要 b c 之间没有引用关系,b c 的执行顺序是不定的
在 Go1.21 及之后:
- 对于无引用关系的包(即 Go1.20 及之前的中的第 2 点),按照其包名字母序决定引用顺序(例如 a 一定在 b 之前执行,github.com/xxx/xxx 一定在 gitlab.com/xxx/xxx 之前执行)
特殊注意:
- 如果 init 存在阻塞,那么用于运行 init 的 goroutine 可能创建新的 goroutine,这会导致某些 init 代码并发运行
- 存在阻塞的情况下,不会保证无引用关系的 package 的 init 完成先后顺序(参考示例 c)
- 存在阻塞的情况下,如果 package a 依赖了 package b,那么 a 的 init 一定在 b 的 init 运行完成后开始运行(参考示例 d)
示例项目
https://github.com/singee-study/go-init