主goroutine和主线程的详细讲解

主goroutine是主线程的意思?

回答:不是

严格说,Go 里没有"主线程"这个概念;

"主goroutine"和"主线程"是两个层次的东西。

主 goroutine 和 主线程 的区别

概念 本质 在 Go 中的情况
主 goroutine 执行 main.main() 的那个协程 确实存在,每个 Go 程序都有
主线程 进程启动时的那个 OS 线程 不算 Go 的概念,Go 程序启动时也会有 OS 线程,但不是"一个固定的主线程"

主 goroutine 运行在操作系统创建的OS线程上

程序启动时,Go 运行时会创建一个 OS 线程(M)初始 OS 线程其实由操作系统创建

然后在它上面运行主goroutine(G),

主goroutine 再调用main() :

复制代码
OS 线程(M)  ← 由操作系统调度到 CPU 核心
   └── 主goroutine(G) ← 执行 main.main()

所以:

  • 主goroutine 运行在某个 OS 线程上 ,但它(主goroutine)本身不是线程
  • 主goroutine 可以被换到别的 OS 线程上继续执行(Go 运行时调度决定的)
  • 说"主线程"只是通俗说法,不严谨的叫法,go中压根没有主线程这个概念。
    如果你非要说主线程;可以理解为:"主 goroutine"

特别说明:

1,初始 OS 线程其实由操作系统创建

2,主 goroutine 不固定绑定在这个线程上

更加准确的说明

程序启动时,操作系统先创建进程及其初始 OS 线程(M)

Go 运行时在该线程上初始化,并创建主 goroutine(G)

由主goroutine 再调用main()

主goroutine运行在这个 OS 线程上,但可能被调度器迁移到其他 M中去;

主goroutine 运行在某个 OS 线程上 ,但它(主goroutine)本身不是线程

整句话的意思是:

主 goroutine 只是"跑在"某个 OS 线程上面的一个执行单元 ,

但这个执行单元自己并不是一个线程。

复制代码
OS 线程(M)  ← 由操作系统调度到 CPU 核心
   └── 主goroutine(G) ← 执行 main.main()

打个比方

复制代码
OS 线程  = 铁轨上的火车(操作系统能调度的实体)
goroutine = 火车上拉的一节货物(Go 运行时调度的逻辑单元)

货物(goroutine) 搭乘 火车(OS 线程)才能前进

但货物本身不是火车 ------ 它可以从这列火车卸下来,

换到另一列火车上继续运输(goroutine 可以被调度到不同的 OS 线程上)

也就是说:主 goroutine ≠ 那个 OS 线程,只是暂时"跑在它上面"

有可能被调度器迁移到其他M(OS 线程)中去

goroutine 是逻辑执行流,线程是物理调度实体,二者不是同一个东西。

一个能感受到区别的细节

在 Java 里,主线程结束 = 进程结束;

在 Go 里, 主 goroutine 返回 时,程序会直接退出,

其他还在跑的 goroutine 会被强行终止 :

复制代码
func main() {
	go func() {
		for {
			fmt.Println("子协程还在跑")
			time.Sleep(time.Second)
		}
	}()
	// main 一旦返回,整个程序退出,上面这个 goroutine 也来不及继续
}
结论

"主goroutine" ≈ 运行main 函数的那个协程, 不是主线程 。

它是轻量级协程,只是恰好跑在某个 OS 线程上。

主 goroutine是什么?

主goroutine 是 Go 运行时创建、用来执行这个函数的那条执行流。

func main() { ... } 是 函数 (入口函数)

两者是"执行者"和"被执行的代码"的关系,不是同一个东西:

复制代码
主 goroutine(执行者)
    └── 被执行 func main() 里的代码

主 goroutine 的特点

主 goroutine 本身 没有特殊能力 ,

和其他 goroutine 一样是轻量级协程,

唯一特殊的是: 它一返回,程序就结束

func main()可以启动其他goroutine一起并行或者并发运行

func main() 里可以用go 启动其他 goroutine,

它们和主 goroutine 并行/并发运行