1、进程的演变
早期的单进程系统
每一个程序就是一个进程,直到程序运行完才能执行下一个进程
想象一下、当你听音乐的时候不能玩游戏、也不能干其他事情、是不是很苦恼、这个就是单进程系统的缺点;还有一种情况是当你要下载一个很大的电影、你甚至可能要等待好几个小时、而这几个小时内、你的你的cpu除了在等待、除了极偶尔地检查一下数据包是否到达、其他时间几乎什么也不做、他的计算能力就被浪费了。
因此、单进程系统的缺点:
1.单一的执行流程
2.进程阻塞带来的cpu时间浪费
多进程系统时代
聪明的人类当然不会被局限、要知道、不仅在自然界、在任何地方、迭代现象几乎无时无刻不在发生、只是有些变化很微小以至于我们察觉不到。
后来、人们在单进程系统的基础上、研发出了多进程系统(故事发展暂不深究、我们这里只讨论技术演变)
那么、什么是多进程系统呢?
简单来说、就是你不仅可以边听音乐边编写文档、甚至你在下载电影的时候依然可以利用cpu去进行某些计算、那么这种多进程系统的能力又是怎么实现的呢?
1.当一个进程阻塞的时候,切换到另外等待执行的进程,能够尽量利用cpu,提高工作效率
也就是说当我们下载电影的时候、系统先保存当前下载电影的进度、先存档、然后切换到其他进程、这样两个进程看起来就好像同步在进行。
当然、这种方式也会有一些问题、比如进程的切换调度开销太大、要知道、一个进程是包含很多东西的、要保存这些状态、然后切换到另一个进程、所需要的开销实在是太大了、就好像你搬家一样。
如何提高cpu的利用率
当进入多核时代之后、每台电脑上面都有多个cpu、我们可以在电脑上面同时执行多个进程、不仅可以一边听音乐一遍打游戏甚至还可以同时挂着微信和qq。
那么如何能够最大化的利用cpu、同时又能够尽量减小线程切换所带来的开销呢?
golang采用了一种经典思想(加一层)把线程分成内核态与用户态、也就是线程和协程、线程切换开销不是大嘛、我们在线程之下分割出更小的处理单元就可以了。假设原来是搬家的话、那现在就是只搬一些小家具
2、线程与协程的绑定关系
1:N
缺点:
1.某个程序用不了硬件的多核加速能力、一个线程无法利用多核的能力
2.当一个协程阻塞,会造成线程阻塞,其他协程无法工作,导致没有并发能力
1:1
缺点:
协程的创建、删除和切换都由cpu完成,开销太大M:N线程由cpu调度是抢占式的,协程由用户调度是协作式的需要针对
M:N
M:N模式既能够利用多核能力、当协程阻塞的时候、线程也可以去处理其他的协程、但是它们中间的关系如何进行维护呢
一层不行再加一层(我们在线程与协程之间添加一个调度去负责维护它们之间的绑定关系)
模型在中间层设计一个调度器goroutine(只占几kb、动态的):让一组可以复用的函数运行在一组线程之上,即使协程阻塞,该线程的其他协程也可以被runtime调度,从而转移到其他可运行的线程上
3、gmp模型

(图片来源于网络)
3.1、gmp模型的组成
g代表goroutine,p代表处理器,m代表内核线程
全局队列:存放等待运行的goroutinr
P的本地队列:每个p单独维护一个自己的本地队列,不超过256个,新建g的时候,如果p的本地队列已经满了,就会把p的本地队列的一半的g转移到全局队列
p列表:所有的p都在程序启动时创建,并保存在数组中,最多有gomaxprocs个
m:由go语言本身的限制决定,默认是10000个
m的休眠队列:如果有休眠的m放在这里
3.2、gmp的执行
1.每一个m想要执行g都要先与P进行绑定
2.由m从p的本地队列中弹出一个goroutine来执行
3.如果p的本地队列没有g了,m会尝试从全局队列进行获取,如果全局队列中也没有可供执行的g,会从其他p维护的本地队列中偷一半的g来执行。
4.如果本线程因为g进行系统调用阻塞时,线程会释放绑定的p,把p转移给其他空闲的线程执行,能够提高资源利用率。
5.如果m不够的话,会尝试从休眠队列中唤醒一个m来与p绑定,如果休眠队列中没有m,就会重新创建一个m
4、gmp如何提高cpu利用率
4.1、hand off机制
如果本线程因为g进行系统调用阻塞时,线程会释放绑定的p,把p转移给其他空闲的线程执行,能够提高资源利用率。
4.2、work stealing
如果p的本地队列没有g了,m会尝试从全局队列进行获取,如果全局队列中也没有可供执行的g,会从其他p维护的本地队列中偷一个g来执行。
go语言中的goroutine是抢占式的,在go中一个goroutine最多占用 cpu10ms
好了、我们今天的分享就到这里、如果小伙伴们有任何的意见或者建议、欢迎评论与我分享!!
博客的配图还没想好用什么画、回头我会补上的