我试图找到一个关于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
似乎init()函数以词汇文件名顺序执行.Go规范说"鼓励构建系统以词汇文件名顺序向编译器提供属于同一个包的多个文件".似乎go build以这种方式工作.
wea*_*ing 185
看到这张照片.:)
import --> const --> var --> init()

如果包导入其他包,则首先初始化导入的包.
然后初始化当前包的常量.
然后初始化当前包的变量.
最后,init()调用当前包的功能.
一个包可以有多个init函数(在单个文件中或分布在多个文件中),并按照它们呈现给编译器的顺序调用它们.
即使从多个包导入包,也只会初始化一次包.
小智 26
要添加的东西(我会在评论中添加,但写这篇文章的时间我还没有足够的声誉)
在同一个包中有多个inits我还没有找到任何保证的方式来知道它们将以什么顺序运行.例如,我有:
package config
- config.go
- router.go
Run Code Online (Sandbox Code Playgroud)
这两个config.go和router.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.go或config.go,之前它会运行init()运行.
这是另一个例子 - 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)
init()函数什么时候运行?
使用 Go 1.16(2021 年第一季度),您将准确了解它何时运行以及运行多长时间。
见提交7c58ef7从CL(更改列表)254659,修复问题41378。
运行时:实现
GODEBUG=inittrace=1支持设置
inittrace=1会导致运行时为每个具有 init 工作的包发出单行标准错误,总结执行时间和内存分配。发出的
init函数调试信息可用于查找 Go 启动性能中的瓶颈或回归。没有
init功能工作(用户定义或编译器生成)的包被省略。不支持跟踪插件初始化,因为它们可以同时执行。这将使跟踪的实现更加复杂,同时增加对非常罕见的用例的支持。可以通过测试显式导入插件包导入的主包来单独跟踪插件初始化。
Run Code Online (Sandbox Code Playgroud)$ 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 ...受到 stapelberg@google.com 的启发,他
doInit在原型中init使用 GDB测量时间。
还有一些提示:
- main() 函数只能有 1 个,但 init() 函数可以有多个。
- 您不需要显式调用 init() 或 main(),它们会自动调用。
- init() 和 main() 不接受任何参数,也不返回任何内容。
- init() 在 main() 之前运行。
- 如果您有多个 init(),它们将按照声明的顺序运行。