重要概念介绍
并发和并行最开始都是操作系统中的概念,表示的是CPU执行多个任务的方式。
并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。从宏观上看是同时执行,微观上仍是顺序执行
并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)
进程,是操作系统资源分配的基本单位,每一个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销。
线程,是“轻量级的进程”,它是CPU任务调度和执行的基本单位。
协程,是用户态的线程。协程没有线程的上下文切换消耗。协程的调度切换是用户(程序员)手动切换的,更加灵活,因此又叫用户空间线程。
goroutine 简介
goroutine是Go语言中轻量级线程的实现。由Go运行时(runtime)管理。
Go程序从main包的main()函数开始,在程序启动时,就会为main()函数创建一个默认的goroutine。
goroutine的使用方式非常简单,只需要通过go关键字来创建一个协程
go func() // 通过go关键字启动一个协程来运行函数
goroutine 调度原理
GMP调度模型
Go调度器由四个重要部分组成
- G:goroutine,代表一个协程,它拥有自己的寄存器上下文和栈。对应的数据结构是runtime.g,所有的G则保存在全局变量allgs中
- M:内核级线程,一个M代表一个线程,goroutine就运行在M之上。对应的数据结构是runtime.m,所有的M则保存在全局变量allm中
- P:Processor,处理器,主要用来执行goroutine,同时维护了一个等待执行的goroutine队列。对应的数据结构是runtime.p,所有的P则保存在全局变量allp中
- Sched:调度器,是一个全局标量。记录着所有空闲的M,空闲的P等许多和调度相关的内容。对应的数据结构是runtime.schedt
调度流程
在进行初始化过程中,会通过GOMAXPROCS这个环境变量决定创建多少个P,而所有的P则保存在全局变量allp中。
上文中提到,每个处理器P维护了一个等待执行的G队列runq,如果P的本地队列已满,那么等待执行的G就会被放入全局队列中。而M会先从关联P的本地队列中获取等待执行的G。没有的话,则到调度器持有的全局队列中获取一些任务。如果全局队列中也没有了,则会去别的P中”分担“一些G过来。
通过上述的介绍,我们现在就可以更好的理解这个经典的示意图了