我试图找到一个关于init()函数在Go中的作用的精确解释。我读了Effective Go说的话,但我不确定我是否完全理解了它说的话。我不太确定的句子是:
finally的意思是最后:init是在包中所有变量声明都计算了它们的初始化式之后调用的,并且只有在所有导入的包都初始化之后才会计算这些初始化式。
包中所有变量声明对其初始化式求值意味着什么?这是否意味着如果你在一个包和它的文件中声明“全局”变量,init()将不会运行,直到所有的它被评估,然后它将运行所有的init函数,然后main()当。/main_file_name运行?
我还读了Mark Summerfield的书:
如果一个包有一个或多个init()函数,它们会在主包的main()函数被调用之前自动执行。
在我的理解中,init()只在您打算运行main()时才相关,对吗?如果有人更清楚地理解init(),请随意纠正我
init()函数何时运行?
使用Go 1.16 (Q1 2021),您将准确地看到它运行的时间以及运行多长时间。
参见从CL(更改列表)254659提交7c58ef7,修复问题41378。
Runtime: implement GODEBUG=inittrace=1 support
Setting inittrace=1 causes the runtime to emit a single line to standard error for
each package with init work, summarizing the execution time and memory allocation.
The emitted debug information for init functions can be used to find bottlenecks
or regressions in Go startup performance.
Packages with no init function work (user defined or compiler generated) are omitted.
Tracing plugin inits is not supported as they can execute concurrently. This would
make the implementation of tracing more complex while adding support for a very rare
use case. Plugin inits can be traced separately by testing a main package importing
the plugins package imports explicitly.
$ 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
...
Inspired by stapelberg@google.com who instrumented doInit
in a prototype to measure init times with GDB.
是的,假设你有这个:
var WhatIsThe = AnswerToLife()
func AnswerToLife() int { // 1
return 42
}
func init() { // 2
WhatIsThe = 0
}
func main() { // 3
if WhatIsThe == 0 {
fmt.Println("It's all a lie.")
}
}
AnswerToLife()保证在调用init()之前运行,并且init()保证在调用main()之前运行。
请记住,init()总是被调用,无论是否有main,所以如果你导入一个包,它有init函数,它将被执行。
此外,每个包可以有多个init()函数;它们将按照它们在文件中出现的顺序执行(当然是在所有变量初始化之后)。如果它们跨越多个文件,它们将按照词法文件名的顺序执行(正如@benc指出的那样):
init()函数似乎是按词法文件名顺序执行的。Go规范说:“构建系统被鼓励以词法文件名的顺序将属于同一个包的多个文件呈现给编译器”。似乎go build是这样工作的。
许多内部Go包使用init()来初始化表等,例如https://github.com/golang/go/blob/883bc6/src/compress/bzip2/bzip2.go#L480
补充一点(我本可以作为评论添加的,但在写这篇文章的时候,我还没有足够的声誉)
在同一个包中有多个初始化,我还没有找到任何保证的方法来知道它们将以什么顺序运行。例如,我有:
package config
- config.go
- router.go
这两个配置。去找路由器吧。Go包含init()函数,但当运行router。Go的函数先运行(这导致我的应用程序恐慌)。
如果您有多个文件,每个文件都有自己的init()函数,请注意不能保证一个文件先于另一个文件。最好使用变量赋值,就像OneToOne在他的例子中展示的那样。最好的部分是:这个变量声明将发生在包中的ALL init()函数之前。
例如
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
}
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)
}
}
}
不管var ConfigSuccess = configureApplication()是否存在于路由器中。Go或config。go,它将在EITHER init()运行之前运行。