init()函数何时运行?

Pin*_*hio 324 init go

我试图找到一个关于init()Go中函数功能的精确解释.我读到了Effective Go所说的内容,但我不确定我是否完全理解它所说的内容.我不确定的确切句子如下:

最后意味着:在包中的所有变量声明都已经评估了它们的初始值设定项之后调用init,并且只有在初始化了所有导入的包之后才会对它们进行求值.

什么all the variable declarations in the package have evaluated their initializers意思?这是否意味着如果在包及其文件中声明"全局"变量,init()将不会运行,直到所有它都被评估,然后它将运行所有init函数,然后运行./main_file_name时运行main()?

我还阅读了Mark Summerfield的以下书:

如果一个包有一个或多个init()函数,它们会在调用主包的main()函数之前自动执行.

根据我的理解,init()只有在你打算运行main()时才有意义吗?或主包.任何人init()都能更准确地理解我

One*_*One 404

是的,假设你有这个:

var WhatIsThe = AnswerToLife()

func AnswerToLife() int {
    return 42
}

func init() {
    WhatIsThe = 0
}

func main() {
    if WhatIsThe == 0 {
        fmt.Println("It's all a lie.")
    }
}
Run Code Online (Sandbox Code Playgroud)

AnswerToLife()保证在init()调用之前运行,并init()保证在main()调用之前运行.

请记住,init()总是会调用它,无论是否有main,所以如果导入具有init函数的包,它将被执行.

//编辑

另外,请记住,init()每个包可以有多个函数,它们将按照它们在代码中显示的顺序执行(当然,在所有变量初始化之后).

//编辑2x

许多内部Go包用于init()初始化表等,例如https://github.com/golang/go/blob/883bc6/src/compress/bzip2/bzip2.go#L480

//编辑3x

@benc在同一个包执行顺序中的多个inits:

似乎init()函数以词汇文件名顺序执行.Go规范说"鼓励构建系统以词汇文件名顺序向编译器提供属于同一个包的多个文件".似乎go build以这种方式工作.

  • 如果你需要初始化几个变量或加载一些文件或进行一次性计算,那么在没有包含`main`的情况下运行init有很多原因.现在,如果您的整个程序是一个并不真正需要的程序包,但是如果它是多个程序包,则其中一些程序可能需要进行一些特定的初始化. (4认同)
  • @Pinocchio执行go程序时总会有一个(只有一个)main().init()函数在一个main()之前运行.但是并非所有包都需要main().例如,如果您正在创建具有用于连接到数据库的实用程序函数的可重用包,则该包不应具有main().但它可能有一个init().当您在程序中使用该数据库包时,该程序将具有main(). (4认同)
  • 我认为 `init()` 是每个包的事情......因此,这是否意味着如果不同的文件有自己的 `inits`,所有 inints 总是在 main() 运行之前运行?您还能否向我澄清一件事,为什么您会有一个没有 main 的 init() 以及它是如何工作的?这是否意味着 init() 是导入包之前运行的最后一件事?否则,如果它没有导入并且没有 main,程序将永远不会执行......对吗?(除非它是一个测试文件,我猜......) (2认同)
  • @OneOfOne 经过几次测试后,似乎 *init()* 函数是按词法文件名顺序执行的。[Go 规范](https://golang.org/ref/spec#Package_initialization) 表示“鼓励构建系统以词法文件名顺序向编译器呈现属于同一包的多个文件”。看起来 *go build* 就是这样工作的。 (2认同)

wea*_*ing 185

看到这张照片.:)

import --> const --> var --> init()

  1. 如果包导入其他包,则首先初始化导入的包.

  2. 然后初始化当前包的常量.

  3. 然后初始化当前包的变量.

  4. 最后,init()调用当前包的功能.

一个包可以有多个init函数(在单个文件中或分布在多个文件中),并按照它们呈现给编译器的顺序调用它们.


即使从多个包导入包,也只会初始化一次包.

  • 谢谢你.添加一些文本对此图有意义. (3认同)
  • 我不太确定我们是否可以说常量已初始化。它们应该在编译时就已知,对吧? (2认同)
  • @KoredeLawrenceOluwafemi 查看图片链接你会发现它来自中国围棋布道者写的一本[书](https://astaxie.gitbooks.io/build-web-application-with-golang/en/) https:// github.com/astaxie (2认同)

小智 26

要添加的东西(我会在评论中添加,但写这篇文章的时间我还没有足够的声誉)

在同一个包中有多个inits我还没有找到任何保证的方式来知道它们将以什么顺序运行.例如,我有:

package config
    - config.go
    - router.go
Run Code Online (Sandbox Code Playgroud)

这两个config.gorouter.go包含init()的功能,但是当运行router.go的功能跑第一(这引起我的应用程序的恐慌).

如果您处于多个文件的情况下,每个文件都有自己的init()功能,请注意您不能保证在另一个文件之前获得一个文件.最好使用OneToOne在他的示例中显示的变量赋值.最好的部分是:此变量声明将init()在包中的ALL 函数之前发生.

例如

config.go:

var ConfigSuccess = configureApplication()

func init() {
    doSomething()
}

func configureApplication() bool {
    l4g.Info("Configuring application...")
    if valid := loadCommandLineFlags(); !valid {
        l4g.Critical("Failed to load Command Line Flags")
        return false
    }
    return true
}
Run Code Online (Sandbox Code Playgroud)

router.go:

func init() {
    var (
        rwd string
        tmp string
        ok  bool
    )
    if metapath, ok := Config["fs"]["metapath"].(string); ok {
        var err error
        Conn, err = services.NewConnection(metapath + "/metadata.db")
        if err != nil {
            panic(err)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不管是否var ConfigSuccess = configureApplication()存在router.goconfig.go,之前它会运行init()运行.

  • 只是我的两分钱:你可以将"简单初始化"与复杂(恐慌提升)初始化分开.如果您对init()感到恐慌,那么您将无法使用主程序.也许``func initialize | loadConfig | connect`与`func init`分开,将func init()留给基本的东西(没有恐慌).这种分离也消除了黑客确保init命令的需要.希望对你有所帮助. (3认同)
  • 从语言规范引用:通过为其所有包级变量分配初始值,然后按照它们在源中出现的顺序(可能在多个文件中)调用所有init函数来初始化没有导入的包,如提供给编译器.为了确保可重现的初始化行为,建议构建系统以词法文件名顺序将属于同一个包的多个文件呈现给编译器. (3认同)

Alo*_*ngh 6

这是另一个例子 - https://github.com/alok87/gobyexample/blob/master/init/init.go

package main

import (
    "fmt"
)

func callOut() int {
    fmt.Println("Outside is beinge executed")
    return 1
}

var test = callOut()

func init() {
    fmt.Println("Init3 is being executed")
}

func init() {
    fmt.Println("Init is being executed")
}

func init() {
    fmt.Println("Init2 is being executed")
}

func main() {
    fmt.Println("Do your thing !")
}
Run Code Online (Sandbox Code Playgroud)

输出上述程序

$ go run init/init.go
Outside is being executed
Init3 is being executed
Init is being executed
Init2 is being executed
Do your thing !
Run Code Online (Sandbox Code Playgroud)


Von*_*onC 6

init()函数什么时候运行?

使用 Go 1.16(2021 年第一季度),您将准确了解它何时运行以及运行多长时间。

提交7c58ef7CL(更改列表)254659,修复问题41378

运行时:实现GODEBUG=inittrace=1支持

设置inittrace=1会导致运行时为每个具有 init 工作的包发出单行标准错误,总结执行时间和内存分配。

发出的init函数调试信息可用于查找 Go 启动性能中的瓶颈或回归。

没有init功能工作(用户定义或编译器生成)的包被省略。

不支持跟踪插件初始化,因为它们可以同时执行。这将使跟踪的实现更加复杂,同时增加对非常罕见的用例的支持。可以通过测试显式导入插件包导入的主包来单独跟踪插件初始化。

$ GODEBUG=inittrace=1 go test
init internal/bytealg @0.008 ms, 0 ms clock, 0 bytes, 0 allocs
init runtime @0.059 ms, 0.026 ms clock, 0 bytes, 0 allocs
init math @0.19 ms, 0.001 ms clock, 0 bytes, 0 allocs
init errors @0.22 ms, 0.004 ms clock, 0 bytes, 0 allocs
init strconv @0.24 ms, 0.002 ms clock, 32 bytes, 2 allocs
init sync @0.28 ms, 0.003 ms clock, 16 bytes, 1 allocs
init unicode @0.44 ms, 0.11 ms clock, 23328 bytes, 24 allocs
...
Run Code Online (Sandbox Code Playgroud)

受到 stapelberg@google.com 的启发,他doInit 在原型中init使用 GDB测量时间。


kin*_*roi 5

还有一些提示:

  • main() 函数只能有 1 个,但 init() 函数可以有多个。
  • 您不需要显式调用 init() 或 main(),它们会自动调用。
  • init() 和 main() 不接受任何参数,也不返回任何内容。
  • init() 在 main() 之前运行。
  • 如果您有多个 init(),它们将按照声明的顺序运行。