神說有光就有光
本篇介紹兩種 Go 做初始化的方式。
init()
作為初始化的工具,任何 .go 檔案裏面都可以有複數個 init() ,執行順序從上到下;任何package被import的時候都會依照import 的順序被初始化一次,main package 最後初始化。
package main
import (
"fmt"
)
var a, b int
func main() {
fmt.Println(a, b) // 1 2
}
func init() {
a = 1
}
func init() {
b = 2
}
某方大大對於init() 的更多探討與優缺,提到了 init() 做了什麼事情、何時何地被呼叫、順序…等,太過隱晦。
根據我找到的這篇,官方提出了一個補救措施,就可以顯示init()被執行的順序以及時間等資訊:
GODEBUG=inittrace=1 go run main.go
結果長這樣
init internal/bytealg @0.010 ms, 0 ms clock, 0 bytes, 0 allocs
init runtime @0.067 ms, 0.10 ms clock, 0 bytes, 0 allocs
init errors @0.77 ms, 0.007 ms clock, 0 bytes, 0 allocs
init sync @0.84 ms, 0.002 ms clock, 16 bytes, 1 allocs
init io @0.87 ms, 0.006 ms clock, 144 bytes, 9 allocs
... 更多
sync.Once
sync.Once 提供了一種叫做 lazy initialization 的機制,因為有些變數初始化之後又用不見得用得到,徒增初始化時間與浪費空間,因此可以延遲到需要用的時候才進行初始化。使用這個方式被呼叫多次的function,只有第一次會真正被執行,後面都會略過:
package main
import (
"fmt"
"sync"
)
var a, b int
var DoOnce sync.Once
func main() {
PrintAB()
PrintAB()
PrintAB()
}
func MyInit() {
fmt.Println("Executing MyInit()")
a = 1
b = 2
}
func PrintAB() {
DoOnce.Do(MyInit)
fmt.Println(a, b)
}
執行結果
Executing MyInit()
1 2
1 2
1 2