misuse init func
今天这篇讲的是 init
函数使用技巧,平时在人家封装好的代码框架中进行开发,很少独立用到 init
函数的地方,其实不小心使用的话坑还是比较多的。
- 一个包中能不能拥有多个
init
init
与全局变量的初始化哪个先执行- 一个包被导入多次
init
是否会执行多次
参考如下代码,你能否说出输出的顺序:
package main |
我是倒在了这上边,不过还好,借这个机会来好好了解一下。我们先看下不包含全局变量的情况,在 main 包中引用 redis 包:
init
函数的执行顺序如上述标号所示,这时候如果一个 package 中有多个 init
函数需要执行时,他们的顺序是什么呢?
假设我们有如下的目录结构,每个文件中都有 init
函数:
hello |
// a.go |
这时候是先执行 a.go
中的 init
还是 b.go
中的呢? **答案是文件名称排序,谁在前边就先执行谁的 init
**。
所以这里警示我们,务必不能通过文件名称的方式确定
init
函数的执行顺序,在不断迭代的过程中文件名称很有可能会被修改。
另一个有意思的地方是,可以在一个文件中写多个 init
函数:
package main |
init
会带来什么样的问题呢?
var db *sql.DB |
通过 init
进行数据库连接的初始化,这里存在至少三个问题:
- 错误处理的局限性,因为
init
是没有参数和返回值的。 - 全局变量的可能会在其它的package中被修改。
- 单元测试的局限性,针对这种
init
函数,我们想进行单元测试不太可能,在执行用例的时候init
函数已经执行完成了。
疑问
假设我们有 package A
,package B
,package main
每个包都有自己的 init
函数,那么他们被导入多次的时候 init
会被执行多次吗?
B 中引用了 A,main 中引用了 B,A;我以为 A 的 init
会被执行两次,又 tm 被打脸了。 不过比较及时,要不面试的时候就尴尬了。
底层实现
// An initTask represents the set of initializations that need to be done for a package. |
这里我理解 runtime_inittask
main_inittask
一个是 runtime 需要用到的初始化函数,一个是 user program 用到的 init 函数,原因如下:cmd/compile/internal/gc/init.go 文件中有关于 init 的代码(省略了部分代码):
// fninit makes an initialization record for the package. |
在初始化的过程中是怎么保证一个包不被执行多次的?
inittask 中有一个表示 init 函数状态的变量,0 代表未执行, 1 代表执行中, 2代表已完成。
本文标题:misuse init func
文章作者:bqyang
发布时间:2022-09-17
最后更新:2023-04-13
原始链接:https://bqyang.top/2022/language/golang/100-go-mistaks/2-misuse-init/
版权声明:The author owns the copyright, please indicate the source reproduced.