Tony Bai · Go 语言第一课
Tony Bai
资深架构师,tonybai.com 博主
21492 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 59 讲
开篇词 (1讲)
结束语 (1讲)
Tony Bai · Go 语言第一课
15
15
1.0x
00:00/00:00
登录|注册

08|入口函数与包初始化:搞清Go程序的执行次序

仅执行一次
顺序执行
无参数无返回值
实现“注册模式”
实现对包级变量的复杂初始化
重置包级变量值
进行包初始化
main函数返回意味着整个程序的终结
在多Goroutine的应用中,main.main函数在主Goroutine中执行
必须定义在main包中
无参数无返回值
用户层执行逻辑的入口函数
特性
用途
特性
实现“注册模式”
实现对包级变量的复杂初始化
重置包级变量值
验证了Go程序启动后,Go包的初始化次序
包内的多个init函数按出现次序进行自动调用
每个包内按以“常量 -> 变量 -> init函数”的顺序进行初始化
依赖包按“深度优先”的次序进行初始化
init函数
main.main函数
init函数的用途
示例程序验证
Go包的初始化次序
入口函数与包初始化
Go程序执行次序

该思维导图由 AI 生成,仅供参考

你好,我是 Tony Bai。
在刚开始学习 Go 语言的时候,我们可能经常会遇到这样一个问题:一个 Go 项目中有数十个 Go 包,每个包中又有若干常量、变量、各种函数和方法,那 Go 代码究竟是从哪里开始执行的呢?后续的执行顺序又是什么样的呢?
事实上,了解这门语言编写应用的执行次序,对我们写出结构合理、逻辑清晰的程序大有裨益,无论你用的是归属为哪种编程范式(Paradigm)的编程语言,过程式的、面向对象的、函数式的,或是其他编程范式的,我都建议你深入了解一下。
所以今天这节课,我就带你来了解一下 Go 程序的执行次序,这样在后续阅读和理解 Go 代码的时候,你就好比拥有了“通往宝藏的地图”,可以直接沿着 Go 代码执行次序这张“地图”去阅读和理解 Go 代码了,不会在庞大的代码库中迷失了。
Go 程序由一系列 Go 包组成,代码的执行也是在各个包之间跳转。和其他语言一样,Go 也拥有自己的用户层入口:main 函数。这节课我们就从 main 函数入手,逐步展开,最终带你掌握 Go 程序的执行次序。
那么下面,我们就先来看看 Go 应用的入口函数。

main.main 函数:Go 应用的入口函数

Go 语言中有一个特殊的函数:main 包中的 main 函数,也就是 main.main,它是所有 Go 可执行程序的用户层执行逻辑的入口函数。Go 程序在用户层面的执行逻辑,会在这个函数内按照它的调用顺序展开。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了Go程序的执行次序和包的初始化次序,通过对入口函数main.main和包初始化函数init的详细讲解,帮助读者快速了解了Go程序的执行逻辑。文章从init函数的用途出发,阐述了init函数在包初始化过程中的重要性,包括重置包级变量值、实现对包级变量的复杂初始化以及在init函数中实现“注册模式”。通过实例代码和流程图,读者可以清晰地了解Go程序启动后,包的初始化次序是否正确。此外,文章还强调了init函数的特殊行为特征,如执行顺位、仅执行一次以及顺序执行等。总之,本文通过深入浅出的方式,全面介绍了Go程序的执行次序和包的初始化次序,为读者提供了清晰的指导和实用的技术知识。读者可以通过本文快速了解Go程序的执行逻辑,为自己的程序编写提供指导和帮助。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Tony Bai · Go 语言第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(57)

  • 最新
  • 精选
  • andox
    分情况而定 1. 初始化失败的是必要的数据 panic处理 结束进程 1. 初始化失败的是对业务没影响,可成功可失败的 输出warn或error日志 方便定位

    作者回复: 手动点赞!

    2021-10-29
    2
    98
  • 多选参数
    同一个包内有多个源文件的话,这个包是将所有源文件的常量、变量、init() 函数汇集到一起,然后常量-变量-init() 这样的顺序进行初始化,而不是每个源文件走一遍常量-变量-init() 这样的顺序,是嘛?老师

    作者回复: 问题很好。实测情况是go会先按文件传入顺序,分别初始化常量与变量,然后在分别调用各个文件中的init函数。比如说如果一个pkg1有两个文件file1.go和file2.go,那么初始化顺序是:file1中的常量 -> file1中的变量 -> file2中常量 -> file2中变量 -> file1中init函数 -> file2中init函数。

    2021-11-16
    13
    51
  • go 循环依赖是怎么处理的?

    作者回复: go不允许循环依赖。编译器会检测并报错。

    2021-10-29
    2
    25
  • Calvin
    简单做个笔记: - Go 包的初始化次序: 1)依赖包按“深度优先”的次序进行初始化; 2)每个包内按以“常量 -> 变量 -> init 函数”(main.main 函数前)的顺序进行初始化; 3)包内的多个 init 函数按出现次序进行自动调用。 - init 函数常见用途: 1)重置包级变量值; 2)实现对包级变量的复杂初始化; 3)在 init 函数中实现“注册模式”(工厂设计模式)- 空导入。 - init 函数具备的几种行为特征(init 函数十分适合做一些包级数据初始化工作以及包级数据初始状态的检查工作): 1)执行顺位排在包内其他语法元素的后面; 2)每个 init 函数在整个 Go 程序生命周期内仅会被执行一次; 3)init 函数是顺序执行的,只有当一个 init 函数执行完毕后,才会去执行下一个 init 函数。

    作者回复: 手动点赞!

    2021-10-29
    2
    24
  • 谢小路
    初始化 init 函数先于 main 函数执行,项目中如非必要,禁止隐式的 init 初始化,协作人员多了,各种千奇百怪的问题都可能导致程序执行失败。转而使用显式的初始化,直接在 main 函数中调用对应的初始化方法。

    作者回复: 👍

    2022-04-24
    2
    10
  • python玩家一枚
    init失败的话,我感觉一般init中要完成的内容好像都偏向资源属性,如果有必然能成功的默认属性则走默认值并警告,如果是必要资源则不成功会影响后续的运行,这时候应该要直接严重错误告警并终止程序吧

    作者回复: 正确✅

    2021-10-30
    2
    7
  • 布凡
    prog-init-order ├── go.mod ├── main.go ├── pkg1 │ └── pkg1.go ├── pkg2 │ └── pkg2.go └── pkg3 └── pkg3.go 怎样在main.go中引用包 _ "github.com/bigwhite/prog-init-order/pkg1" _ "github.com/bigwhite/prog-init-order/pkg2" 有什么特殊的操作吗?因为我在vscode上事件的时候会提示 “could not import xxx(cannot find package "xxx" in any of C:/Program File\Go(from $GOROOT) ……(from $GOPATH))” 意思是GOPATH中找不这个包,就算我将项目变成了一个Go Module 也依然找不到,是我那里理解的有问题吗?还请老师解惑,main中 import是怎样引用到自己的pkg包中的方法呢?

    作者回复: gopath模式下肯定不行。go module模式下,检查一下go.mod中module path是否是github.com/bigwhite/prog-init-order?

    2021-11-03
    8
    5
  • 进化菌
    真棒,相当于了解go代码的执行生命周期。 当 init 函数在检查包数据初始状态时遇到失败或错误的情况,我们该如何处理呢?直接返回异常吗?在go里面,异常一般会当成第二个返回值吧。

    作者回复: init函数没有返回值,异常是通过panic机制传导的,通常导致程序退出。当 init 函数在检查包数据初始状态时遇到失败或错误的情况,通过panic退出是一个多数的选择。

    2021-10-30
    5
  • CLMOOK🐾
    老师好,有个小疑问,为啥init()允许在一个源文件中有多个,与其他func一样不允许同名不是简单点?go这么设计的出发点是什么? 谢谢

    作者回复: 1. init函数不是普通函数,就和main函数一样,编译器会有特殊关照,一个包里有多个init不会出现“init已定义”的编译错误。 2. 至于允许存在多个init,这个没有标准答案。日常实践中,我会将不同类型的初始化操作放在不同init(初始化的“内聚”),同一包内的不同文件的内的初始化,都放在各自文件中,这样代码可读性好一些。

    2022-09-18归属地:北京
    4
  • lesserror
    Tony Bai 老师中这一讲很精彩,受益良多。 另外有几个问题,麻烦抽空解答一下: 1. 文中说:“非 main 包中自定义的 main 函数仅限于包内使用?” 那我是否可以理解为:main包中定义的 main函数,包外可以使用? 2. 感觉这个课程应该弄个 GitHub 仓库,提交一些本课程中的代码 ? 便于以后进行代码的学习和复习使用? ps: 老师结合标准库源码来讲代码的使用方法非常棒,个人感觉比举例 用类似阿猫、 阿狗之类的代码更利于上手学习。

    作者回复: 1. 不可以。main包中定义的main函数也仅能在main包内使用 2. 好建议。 3. 如果标准库中有合适的例子,我会尽量用标准库的。

    2021-10-30
    2
    4
收起评论
显示
设置
留言
57
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部