Go init()使用详解

985 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

1. init()的使用

见名知意,init()是Go中的初始化函数。我们都知道,main()函数是Go程序启动的入口,而init()函数就是在main()之前,起到一个初始化的作用。

package main

import (
   "fmt"
)

func main() {
   fmt.Println("程序运行......")

}

func init() {
   fmt.Println("初始化......")
}

运行结果:

初始化......
程序运行......

init()的使用非常常见,我们可以在这里进行MySQL,Redis或者MQ等客户端的初始化,方便我们在程序中直接调用次。

2. init()的执行顺序

需要注意的是,每个Go文件中都可以存在多个init()函数,但是统一文件中的init()函数的执行顺序是不固定的。

刚刚我们已经知道了,init()函数是在main()之前运行的,那么多个文件中的init()是怎样的执行顺序呢?

新建两个文件main.gocalled.go

package main

import (
   "fmt"
)

func main() {
   fmt.Println("程序运行......")
}

func init() {
   fmt.Println("初始化......")
}
package main

import "fmt"

func init() {
   fmt.Println("called初始化......")
}

运行main(),输出结果如下:

called 初始化......
初始化......
程序运行......

我们可以看到,导入的包中的init()是优先于本文件的init()执行的。

另外,变化初始化的过程是先与init()的,整体流程如下图所示:

image.png

3. init()与sync.Once对比

在 Go 语言的 sync 包中提供了 sync.Once 原语用于进行运行时的一次性加载过程。

  • sync.Once 是在代码运行中需要的时候执行,且只执行一次。
  • init() 函数是在文件包首次被加载的时候执行,且只执行一次。

sync.Once 仅提供了一个方法 Do,参数 f 是对象初始化函数,其使用如下所示:

func ReadConfig() *Config {
    once.Do(func() {
        var err error
        config = &Config{Server: os.Getenv("TT_SERVER_URL")}
        config.Port, err = strconv.ParseInt(os.Getenv("TT_PORT"), 10, 0)
        if err != nil {
            config.Port = 8080 // default port
        }
        log.Println("init config")
    })
    return config
}

总的来说,init()的初始化过程更像是饿加载,而sync.Once是懒加载。