Go 语言允许同文件定义多个 init 函数,那么它们是按照什么顺序被调用呢?
网络上大多数资料都说:
按照声明顺序进行调用(实测结果也是这样)
但也有少量帖子的说法是:
Go 语言没有明确定义这个顺序,因此不建议依赖其调用顺序进行编程(我也的确没找到官方文档提到这一点)
所以哪种说法比较正确?官方到底有没有明确定义呢?
已经有答案了,的确是我眼瞎。
calling all init functions in the order they appear in the source
1
mengzhuo 2019-04-02 09:52:57 +08:00
是函数在符号表里的字母顺序
特殊的会提前,比如说 internal/cpu |
2
skiy 2019-04-02 10:08:15 +08:00
没试过,不会有冲突的吗?代码如何的?
|
3
xeaglex OP 可能我讲得不清楚,给个例子吧:
```go // test.go package main import "fmt" func init() { fmt.Println("0") } func init() { fmt.Println("1") } func init() { fmt.Println("2") } func main() { } ``` |
5
yuikns 2019-04-02 10:35:07 +08:00 1
https://golang.org/ref/spec#Package_initialization
> To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler. 这段说明了 init 的调用。 简而言之, 强制: 1. 可以定义多个,即便在同文件 2. 同包不重复初始化 3. 在同一个 goroutine 依次执行所有 init functions 推荐: 构建系统按照字典序依次执行。 因此 init 乱序都没有违规,更何况同文件的 init。 因此你这个只是 ub,只是恰好按照你期望执行。 如果你真的需要有序。你可以一个 init 依次调用就行 |
6
xpfd 2019-04-02 10:40:34 +08:00 1
想不明白为什么要定义多个 init 函数然后把执行顺序交给系统,定义一个 init 然后依次调用其他的不挺好吗? 这样做有什么好处? 一个 c 程序员的疑惑
|
7
0ZXYDDu796nVCFxq 2019-04-02 10:41:29 +08:00 via Android 1
这种不太明确的代码就不要写也不用管怎么实现的吧
就如#5 所说,用 func init 调用就行了 |
8
0ZXYDDu796nVCFxq 2019-04-02 10:43:23 +08:00 via Android
这种多个 func init 感觉就是茴香豆的茴字的四种写法
|
9
whitehack 2019-04-02 10:43:54 +08:00
|
10
yuikns 2019-04-02 10:55:22 +08:00
@xpfd 我理解,它相当于是一个 package 级别的 loader。多线程程序到这儿可以简单同步一下。比 C 少一个全局的 lock 或者 static
go 一直致力于简化多线程模型。此处也不例外。 |
12
polythene 2019-04-02 11:03:48 +08:00
看生成的汇编吧,是按定义的顺序执行的。
go tool compile -S test.go |
14
xeaglex OP @polythene 看汇编跟看运行结果基本没有区别,这都只能说明目前编译器的表现,不代表它将来一定会遵守这个规则呀(如果的确是个 ub,而不是我检索能力太弱的话)
|
15
xeaglex OP @xpfd 比如,假如我们要利用 init 初始化很多包环境,可以利用多个 init 来分割多个类别的初始化,这样可以让代码更利于阅读一点。
|
16
BruceAuyeung 2019-04-02 11:42:59 +08:00 2
同文件中的 init 确实是按照声明顺序(从上到下)执行的,这个 spec 有明确定义
>A package with no imports is initialized by assigning initial values to all its package-level variables followed by calling all init functions in the order they appear in the source, possibly in multiple files, as presented to the compiler. 但是不同文件中的 init 的执行顺序则由编译器先收到先执行这个规则来定,问题是,规范并没有定义应该先送哪个文件,仅仅是鼓励按文件名称词法序(即字母序)。 |
17
reus 2019-04-02 12:39:56 +08:00 1
同一个文件从上到下
不同文件顺序不定 表达依赖顺序其实很容易啊,不需要 init var a = func() bool { // ... return true }() var b = func(_ bool) { // ... }(a) 用全局变量实现,因为 b 的计算依赖 a,所以 a 必然先于 b 计算,这个是规范规定的,也符合直觉。编译器会帮你计算依赖,保证按照依赖的顺序做初始化。 |
18
xeaglex OP @BruceAuyeung 还真有这一段,我瞎了
|
19
Wisho 2019-04-02 14:01:56 +08:00
|
20
reus 2019-04-02 16:33:37 +08:00
@Wisho 初始化顺序在规范文档里说得清清楚楚: https://golang.org/ref/spec#Package_initialization
现在的实现,以及未来的实现,都会以这个为依据。写代码来试验,得到的结果很多时候都是“未定义行为”,例如这篇文章里提到的多个文件的顺序,只是建议编译器按照文件名顺序,没有说一定是,不按照这个顺序也是允许的。 这篇文章的结论只对当前编译器的实现有效,如果以后编译器改了,而你的代码依赖这种“未定义行为”,那你的程序就要出错。 |
21
yuikns 2019-04-03 08:30:54 +08:00
@BruceAuyeung 感谢。我看一楼的说法也没看懂,前面看下 dependencies analysis 确认了下后面包内文件顺序不强制就想当然了
|