首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
OPPO Watch
xeaglex
V2EX  ›  程序员

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

  •  
  •   xeaglex · 2019-04-02 09:46:29 +08:00 · 2939 次点击
    这是一个创建于 425 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    网络上大多数资料都说:

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

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

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

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

    第 1 条附言  ·  2019-04-02 13:29:20 +08:00

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

    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-04-02 09:52:57 +08:00
    是函数在符号表里的字母顺序

    特殊的会提前,比如说 internal/cpu
    skiy
        2
    skiy   2019-04-02 10:08:15 +08:00
    没试过,不会有冲突的吗?代码如何的?
    xeaglex
        3
    xeaglex   2019-04-02 10:18:17 +08:00
    可能我讲得不清楚,给个例子吧:

    ```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   2019-04-02 10:18:46 +08:00
    @mengzhuo
    @skiy

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

    go 一直致力于简化多线程模型。此处也不例外。
    yuikns
        11
    yuikns   2019-04-02 10:58:46 +08:00
    @xpfd 不好意思眼残看错了....
    polythene
        12
    polythene   2019-04-02 11:03:48 +08:00
    看生成的汇编吧,是按定义的顺序执行的。

    go tool compile -S test.go
    xeaglex
        13
    xeaglex   2019-04-02 11:10:31 +08:00
    @yuikns 我的看法和你一致,只是网上资料几乎全说会按照声明顺序执行,搞得我不自信了哈哈
    xeaglex
        14
    xeaglex   2019-04-02 11:12:13 +08:00
    @polythene 看汇编跟看运行结果基本没有区别,这都只能说明目前编译器的表现,不代表它将来一定会遵守这个规则呀(如果的确是个 ub,而不是我检索能力太弱的话)
    xeaglex
        15
    xeaglex   2019-04-02 11:20:03 +08:00
    @xpfd 比如,假如我们要利用 init 初始化很多包环境,可以利用多个 init 来分割多个类别的初始化,这样可以让代码更利于阅读一点。
    BruceAuyeung
        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 的执行顺序则由编译器先收到先执行这个规则来定,问题是,规范并没有定义应该先送哪个文件,仅仅是鼓励按文件名称词法序(即字母序)。
    reus
        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 计算,这个是规范规定的,也符合直觉。编译器会帮你计算依赖,保证按照依赖的顺序做初始化。
    xeaglex
        18
    xeaglex   2019-04-02 13:16:14 +08:00
    @BruceAuyeung 还真有这一段,我瞎了
    Wisho
        19
    Wisho   2019-04-02 14:01:56 +08:00
    @xeaglex
    楼主可以看看这篇分析,marcoma.xyz/2019/01/21/go-package-init/
    有多种 case 下的 example code 和理论分析
    reus
        20
    reus   2019-04-02 16:33:37 +08:00
    @Wisho 初始化顺序在规范文档里说得清清楚楚: https://golang.org/ref/spec#Package_initialization

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

    这篇文章的结论只对当前编译器的实现有效,如果以后编译器改了,而你的代码依赖这种“未定义行为”,那你的程序就要出错。
    yuikns
        21
    yuikns   2019-04-03 08:30:54 +08:00
    @BruceAuyeung 感谢。我看一楼的说法也没看懂,前面看下 dependencies analysis 确认了下后面包内文件顺序不强制就想当然了
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2955 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 09:11 · PVG 17:11 · LAX 02:11 · JFK 05:11
    ♥ Do have faith in what you're doing.