Go 语言同文件的多个 init 函数调用顺序? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
xeaglex
V2EX    程序员

Go 语言同文件的多个 init 函数调用顺序?

  •  
  •   xeaglex
    eaglexiang 2019 年 4 月 2 日 5787 次点击
    这是一个创建于 2476 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Go 语言允许同文件定义多个 init 函数,那么它们是按照什么顺序被调用呢?

    网络上大多数资料都说:

    按照声明顺序进行调用(实测结果也是这样)

    但也有少量帖子的说法是:

    Go 语言没有明确定义这个顺序,因此不建议依赖其调用顺序进行编程(我也的确没找到官方文档提到这一点)

    所以哪种说法比较正确?官方到底有没有明确定义呢?

    第 1 条附言    2019 年 4 月 2 日

    已经有答案了,的确是我眼瞎。

    calling all init functions in the order they appear in the source

    见 [golang.org/ref/spec]

    21 条回复    2019-04-03 08:30:54 +08:00
    mengzhuo
        1
    mengzhuo  
       2019 年 4 月 2 日
    是函数在符号表里的字母顺序

    特殊的会提前,比如说 internal/cpu
    skiy
        2
    skiy  
       2019 年 4 月 2 日
    没试过,不会有冲突的吗?代码如何的?
    xeaglex
        3
    xeaglex  
    OP
       2019 年 4 月 2 日
    可能我讲得不清楚,给个例子吧:

    ```go
    // test.go

    package main

    import "fmt"

    func init() {
    fmt.Println("0")
    }

    func init() {
    fmt.Println("1")
    }

    func init() {
    fmt.Println("2")
    }

    func main() {
    }

    ```
    xeaglex
        4
    xeaglex  
    OP
       2019 年 4 月 2 日
    @mengzhuo
    @skiy

    之前说得可能不清楚,我在三楼补充了一个例子
    yuikns
        5
    yuikns  
       2019 年 4 月 2 日   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 依次调用就行
    xpfd
        6
    xpfd  
       2019 年 4 月 2 日   1
    想不明白为什么要定义多个 init 函数然后把执行顺序交给系统,定义一个 init 然后依次调用其他的不挺好吗? 这样做有什么好处? 一个 c 程序员的疑惑
    0ZXYDDu796nVCFxq
        7
    0ZXYDDu796nVCFxq  
       2019 年 4 月 2 日 via Android   1
    这种不太明确的代码就不要写也不用管怎么实现的吧
    就如#5 所说,用 func init 调用就行了
    0ZXYDDu796nVCFxq
        8
    0ZXYDDu796nVCFxq  
       2019 年 4 月 2 日 via Android
    这种多个 func init 感觉就是茴香豆的茴字的四种写法
    yuikns
        10
    yuikns  
       2019 年 4 月 2 日
    @xpfd 我理解,它相当于是一个 package 级别的 loader。多线程程序到这儿可以简单同步一下。比 C 少一个全局的 lock 或者 static

    go 一直致力于简化多线程模型。此处也不例外。
    yuikns
        11
    yuikns  
       2019 年 4 月 2 日
    @xpfd 不好意思眼残看错了....
    polythene
        12
    polythene  
       2019 年 4 月 2 日
    看生成的汇编吧,是按定义的顺序执行的。

    go tool compile -S test.go
    xeaglex
        13
    xeaglex  
    OP
       2019 年 4 月 2 日
    @yuikns 我的看法和你一致,只是网上资料几乎全说会按照声明顺序执行,搞得我不自信了哈哈
    xeaglex
        14
    xeaglex  
    OP
       2019 年 4 月 2 日
    @polythene 看汇编跟看运行结果基本没有区别,这都只能说明目前编译器的表现,不代表它将来一定会遵守这个规则呀(如果的确是个 ub,而不是我检索能力太弱的话)
    xeaglex
        15
    xeaglex  
    OP
       2019 年 4 月 2 日
    @xpfd 比如,假如我们要利用 init 初始化很多包环境,可以利用多个 init 来分割多个类别的初始化,这样可以让代码更利于阅读一点。
    BruceAuyeung
        16
    BruceAuyeung  
       2019 年 4 月 2 日   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 的执行顺序则由编译器先收到先执行这个规则来定,问题是,规范并没有定义应该先送哪个文件,仅仅是鼓励按文件名称词法序(即字母序)。
    reus
        17
    reus  
       2019 年 4 月 2 日   1
    同一个文件从上到下

    不同文件顺序不定

    表达依赖顺序其实很容易啊,不需要 init

    var a = func() bool {
    // ...
    return true
    }()

    var b = func(_ bool) {
    // ...
    }(a)

    用全局变量实现,因为 b 的计算依赖 a,所以 a 必然先于 b 计算,这个是规范规定的,也符合直觉。编译器会帮你计算依赖,保证按照依赖的顺序做初始化。
    xeaglex
        18
    xeaglex  
    OP
       2019 年 4 月 2 日
    @BruceAuyeung 还真有这一段,我瞎了
    Wisho
        19
    Wisho  
       2019 年 4 月 2 日
    @xeaglex
    楼主可以看看这篇分析,marcoma.xyz/2019/01/21/go-package-init/
    有多种 case 下的 example code 和理论分析
    reus
        20
    reus  
       2019 年 4 月 2 日
    @Wisho 初始化顺序在规范文档里说得清清楚楚: https://golang.org/ref/spec#Package_initialization

    现在的实现,以及未来的实现,都会以这个为依据。写代码来试验,得到的结果很多时候都是“未定义行为”,例如这篇文章里提到的多个文件的顺序,只是建议编译器按照文件名顺序,没有说一定是,不按照这个顺序也是允许的。

    这篇文章的结论只对当前编译器的实现有效,如果以后编译器改了,而你的代码依赖这种“未定义行为”,那你的程序就要出错。
    yuikns
        21
    yuikns  
       2019 年 4 月 3 日
    @BruceAuyeung 感谢。我看一楼的说法也没看懂,前面看下 dependencies analysis 确认了下后面包内文件顺序不强制就想当然了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2630 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 02:30 PVG 10:30 LAX 18:30 JFK 21:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86