go 入门第五天 了解Go程序的执行次序

编程入门 行业动态 更新时间:2024-10-12 05:50:58

go 入门第五天 了解Go程序的执行<a href=https://www.elefans.com/category/jswz/34/1731611.html style=次序"/>

go 入门第五天 了解Go程序的执行次序

Go 应用的入口函数 main.main

package main
func main() {//do nothing
}

1.可执行程序的 main 包必须定义 main 函数,否则 Go 编译器会报错

PS D:\coder\goprojects\initorder> go run main.go
# command-line-arguments
runtime.main_main·f: function main is undeclared in the main package

2.非main 包中自定义的 main 函数仅限于包内使用
3.main 包是应用的入口函数,有可能不是第一位置执行的,如果main 包中定义了 init 函数或main包中加载了其他依赖包中 定义了init 函数,会优先调用init 函数:Go 包的初始化函数

一个 Go 包的初始化是以何种次序和逻辑进行的

package mainimport ("fmt"_ "initorder/pkg1"_ "initorder/pkg2"
)var (_  = constInitCheck()v1 = variableInit("v1")v2 = variableInit("v2")
)const (c1 = "c1"c2 = "c2"
)func constInitCheck() string {if c1 != "" {fmt.Println("main: const c1 has been initialized")}if c2 != "" {fmt.Println("main: const c2 has been initialized")}return ""
}func variableInit(name string) string {fmt.Printf("main: var %s has been initialized\n", name)return name
}func init() {fmt.Println("main: first init func invoked")
}func init() {fmt.Println("main: second init func invoked")
}func main() {
do nothing
}

当前只列举了main.go 文件的内容,pkg1/pkg1.go pkg2/pkg2.og 以及 pgk3/pgk3.go 对应的文件不在列举,他和main.go 的文件基本一致,只有加载的包不同,以及输入的标识不同,当前测试结构是 main.go main 包 import pkg1 包 和pkg2 下面的包,pkg1 和pkg2 下面的 import 了pkg3目录下得包,对应打印输入我们看下他的执行顺序

PS D:\coder\goprojects\initorder> go run main.go
pkg3: const c1 has been initialized
pkg3: const c2 has been initialized
pkg3: var v1 has been initialized
pkg3: var v2 has been initialized
pkg3: first init func invoked
pkg3: second init func invoked
pkg1: const c1 has been initialized
pkg1: const c2 has been initialized
pkg1: var v1 has been initialized
pkg1: var v2 has been initialized
pkg1: first init func invoked
pkg1: second init func invoked
pkg2: const c1 has been initialized
pkg2: const c2 has been initialized
pkg2: var v1 has been initialized
pkg2: var v2 has been initialized
pkg2: first init func invoked
pkg2: second init func invoked
main: const c1 has been initialized
main: const c2 has been initialized
main: var v1 has been initialized
main: var v2 has been initialized
main: first init func invoked
main: second init func invoked

####分析执行顺序

import ("fmt"_ "initorder/pkg1"_ "initorder/pkg2"
)

main包main.go文件中 都依赖 pkg1 和pkg2 下面的依赖包,
pgk1 和pkg2都依赖了pkg3 下面的依赖包,

1.执行顺序main.go是 从上到下 先执行加载 pkg1 下面的依赖包
2.加载pkg1依赖包后发现 pkg1 加载了了 pkg3 下依赖包 优先执行pkg3依赖包 pkg3 执行 常量 -> 变量 -> init 函数
3.pkg3包执行完接着又回pkg1包,pkg1包 执行 常量 -> 变量 -> init 函数
4.pkg1包执行完接着回main包 发现还加载了 pkg2 下面的依赖包
5.pkg2下面依赖包 也 加载了 pkg3 下面的依赖包,但是没有 pkg3的执行日志记录,(pkg3已经被加载不在重新被执行)
6. pkg2包 执行 常量 -> 变量 -> init 函数
7. pkg2包 执行完回到main包执行 常量 -> 变量 -> init 函数 ->main 函数

go初始化包执行规律

依赖包按“深度优先”的次序进行初始化;
每个包内按以“常量 -> 变量 -> init 函数”的顺序进行初始化;
包内的多个 init 函数按出现次序进行自动调用。

Go 包的初始化函数 init 用途

通过go初始化包执行次序 每个包内按以“常量 -> 变量 -> init 函数”的顺序进行初始化,可以在init 函数中对包中得变量常量 做更改

重置包级变量值
是实现对包级变量的复杂初始化

标准库 http 包中就有这样一个典型示例


var (http2VerboseLogs    bool // 初始化时默认值为falsehttp2logFrameWrites bool // 初始化时默认值为falsehttp2logFrameReads  bool // 初始化时默认值为falsehttp2inTests        bool // 初始化时默认值为false
)func init() {e := os.Getenv("GODEBUG")if strings.Contains(e, "http2debug=1") {http2VerboseLogs = true // 在init中对http2VerboseLogs的值进行重置}if strings.Contains(e, "http2debug=2") {http2VerboseLogs = true // 在init中对http2VerboseLogs的值进行重置http2logFrameWrites = true // 在init中对http2logFrameWrites的值进行重置http2logFrameReads = true // 在init中对http2logFrameReads的值进行重置}
}

通过环境变量对bool 变量开关做修改

在 init 函数中实现“注册模式”

lib/pq 包访问 PostgreSQL 数据库的代码示例:


import ("database/sql"_ "github/lib/pq"
)func main() {db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full")if err != nil {log.Fatal(err)}age := 21rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)...
}

发现 我们空导入PostgreSQL 包也可以访问,对应的包内方法,原来是PostgreSQL 包 对应的init 函数中注册了 访问的实例,所以直接可以在空导入的时候调用 对应的方法

func init() {sql.Register("postgres", &Driver{})
}

这种“注册模式”实质是一种工厂设计模式的实现,sql.Open 函数就是这个模式中的工厂方法,它根据外部传入的驱动名称“生产”出不同类别的数据库实例句柄。

这种通过在 init 函数中注册自己的实现的模式,就有效降低了 Go 包对外的直接暴露,尤其是包级变量的暴露,从而避免了外部通过包级变量对包状态的改动。

好记性不如烂笔头,本文学自 极客时间 Tony Bai · Go 语言第一课

更多推荐

go 入门第五天 了解Go程序的执行次序

本文发布于:2024-03-14 15:04:26,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1736733.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:次序   第五天   入门   程序

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!