进程:独立分配系统资源,不与其他进程共享堆栈
线程:线程是进程的一个实体(执行体),是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。同一个进程下的线程共享堆,不共享栈
协程:和线程类似,进程线程的切换主要依赖于时间片的轮转,而协程切换主要依赖于自身。
goroutine: go语言实现的协程,并且是go原生支持的(通过runtime实现),协程算是线程下的,跟线程和进程的关系一样,这是我个人的理解,要是错了不负责啊。每个协程初始时堆栈4k,会随着运行变大和回收。
Goroutine的实现原理
- 两种备选方案
- (M:1)多个用户态的线程对应一个系统线程,它可以做快速的上下文切换。缺点是不能有效利用多核CPU
- (1:1)一个用户态的线程对应一个系统线程,它可以利用多核机制,但上下文切换需要消耗额外的资源
Golang的做法
- (M:N)Golang采取了一种多对多的方案。M个用户线程对应N个系统线程,缺点增加了调度器的实现难度
那么go中切换goroutine的调度点有哪些呢?具体有以下三种情况
- 调用runtime·gosched函数。goroutine主动放弃CPU,该goroutine会被设置为runnable状态,然后放入一个全局等待队列中,而P将继续执行下一个goroutine。使用runtime·gosched函数是一个主动的行为,一般是在执行长任务时又想其它goroutine得到执行的机会时调用。
- 调用runtime·park函数。goroutine进入waitting状态,除非对其调用runtime·ready函数,否则该goroutine将永远不会得到执行。而P将继续执行下一个goroutine。使用runtime·park函数一般是在某个条件如果得不到满足就不能继续运行下去时调用,当条件满足后需要使用runtime·ready以唤醒它(这里唤醒之后是否会加入全局等待队列还有待研究)。像channel操作,定时器中,网络poll等都有可能park goroutine。
- 慢系统调用。这样的系统调用会阻塞等待,为了使该P上挂着的其它G也能得到执行的机会,需要将这些goroutine转到另一个OS线程上去。具体的做法是:首先将该P设置为syscall状态,然后该线程进入系统调用阻塞等待。之前提到过的sysmom线程会定期扫描所有的P,发现一个P处于了syscall的状态,就将M和P分离(实际上只有当 Syscall 执行时间超出某个阈值时,才会将 M 与 P 分离)。RUNTIME会再分配一个M和这个P绑定,从而继续执行队列中的其它G。而当之前阻塞的M从系统调用中返回后,会将该goroutine放入全局等待队列中,自己则sleep去。
那就会有个问题,如果一个系统调用或者G任务执行太长,他就会一直占用这个线程,由于本地队列的G任务是顺序执行的,其它G任务就会阻塞了,怎样中止长任务的呢?(这个地方我找了好久~o(╯□╰)o)
这样滴,启动的时候,会专门创建一个线程sysmon,用来监控和管理,在内部是一个循环:
1. 记录所有P的G任务计数schedtick,(schedtick会在每执行一个G任务后递增)
2. 如果检查到 schedtick一直没有递增,说明这个P一直在执行同一个G任务,如果超过一定的时间(10ms),就在这个G任务的栈信息里面加一个标记
3. 然后这个G任务在执行的时候,如果遇到非内联函数调用,就会检查一次这个标记,然后中断自己,把自己加到队列末尾,执行下一个G
调度点的情况说清楚了,但整个模型还并不完整。我们知道当使用go去调用一个函数,会生成一个新的goroutine放入当前P的队列中,那么什么时候生成别的OS线程,各个OS线程又是如何做负载均衡的呢?
当M从队列中拿到一个可执行的G后,首先会去检查一下,自己的队列中是否还有等待的G,如果还有等待的G,并且也还有空闲的P,此时就会通知runtime分配一个新的M(如果有在睡觉的OS线程,则直接唤醒它,没有的话则生成一个新的OS线程)来分担任务。
如果某个M发现队列为空之后,会首先从全局队列中取一个G来处理。如果全局队列也空了,则会随机从别的P那里直接截取一半的队列过来(偷窃任务),如果发现所有的P都没有可供偷窃的G了,该M就会陷入沉睡。
整个调度模型大致就是这样子了,和所有协程的调度一样,在响应时间上,这种协作式调度是硬伤。很容易导致某个协程长时间无法得到执行。但总体来说,它带来的好处更加让人惊叹。想要了解的更多可以看看我下面列出的一些参考资料,或是直接看它的源码:http://golang.org/src/runtime/proc.c
参考链接: https://blog.csdn.net/justaipanda/article/details/44064811