很多人都是冲着GO的高并发来学习Go的,今天我们来解析一下
首先我们先明确两个概念:并发和并行
并发:一个CPU能同时执行多项任务,在很短的时间内,CPU来回切换执行任务,宏观上是同时的,微观上其实还是顺序执行,因为时间差太小,所以看起来就是多个任务同时执行,这就是并发
并行:当系统有多个cpu时,每个cpu同一时刻都运行任务,互不抢占自己所在的资源,同时进行,这就是并行
明确一下:并行不是并发,并行是可以同时做很多事情,并发是可以同时管理很多事情。
我们先介绍一下并发的过程:
创建一个go==>放在全局运行队列中=调度器进行调度分给=>逻辑处理器==>放在本地运行队列中=等待=》执行(逻辑处理器执行)
接下来我们把每个过程都详细说明一下
groutine能拥有强大的并发实现是通过GPM调度模型实现,下面就来解释下goroutine的调度模型。
Go的调度模型:GPM调度模型
Go的调度器内部有四个重要的结构:M,P,S,Sched,如上图所示(Sched未给出)
M:M代表内核级线程,一个M就是一个线程,goroutine就是跑在M之上的;M是一个很大的结构,里面维护小对象内存cache(mcache)、当前执行的goroutine、随机数发生器等等非常多的信息
G:代表一个goroutine,它有自己的栈,instruction pointer和其他信息(正在等待的channel等等),用于调度。
P:P全称是Processor,处理器,它的主要用途就是用来执行goroutine的,所以它也维护了一个goroutine队列,里面存储了所有需要它来执行的goroutine
Sched:代表调度器,它维护有存储M和G的队列以及调度器的一些状态信息等。
定义都了解了,让我们看一下调度的实现
形象化的话,就是上面这种过程,上图中是有两个M,也就是有两个线程,每一个M中存在一个处理器(p),p下面的G是正在执行的协程(goroutine),右面连得的就是本地运行队列,里面就是等待执行的协程,当正在执行的G完成之后,P就会调用队列中的G(队首),所以p就是维护这个队列的或者说是控制。
有人可能会问队列中的G从哪里来的,这就是上面没有体现出来的Sched(调度器),因为他储存着这些信息,所以他会根据不同情况给每个逻辑处理器分配goroutine,而p只需要维护自己的本地运行队列就可以。
从上面我们还可以得出一个结论就是,Go也可以实现并行,通过创建多个逻辑处理器,这样调度器可以同时分配全局队列中的goroutine到不同的逻辑处理器,go如何实现并行呢?
答案很简单就是通过runtime包
runtime.GOMAXPROSS(a int) //设置CPU核数为a,一个CPU可开启一个线程,不设置默认是1
可以通过runtime.NumCPU()来查看本机的CPU核数,不要盲目设置
以上这些就是go并发的大致过程,欢迎大家评论指正!