线程的模型
1.内核态和用户态
这两种状态都代表着线程或进程所处的某种状态
内核态:处于这个状态的线程或者进程可以访问计算机任何资源
用户态:处于这个状态的线程或者进程访问权限有限,当需要进行IO等操作需要切换至内核态。
内核态线程:由内核本身启动的线程 用户态线程:处于用户态下的线程,相较内核态更加轻量化。但访问权限有限。
协程就是一种轻量化的用户态线程。
Go的并发模型
1.线程实现模型MPG
G:Goroutine的简称,是对一个要并发执行的任务进行的封装,也可以称作用户态线程。
M:直接关联了一个内核线程。执行G中的并发任务。 此时G通过M的资源已经可以执行并发任务 出现的问题:
1.不同的G在不同的M上运行都需要申请资源(如堆内存),可是资源是全局的,因此需要加锁来解决问题,会造成性能的消耗,因此我们可以给每个M提前申请一片空间,G需要时向M申请。
2.如果因为系统调用M陷入了阻塞,那么其下挂载的G都要被转移到另一个M上进行任务的执行。
因此我们加入了P进行了功能的拆分
P:用来管理G对象,P挂载在M下,G队列挂载在P下 当之前的问题再出现时,GMP模型处理策略如下: 1.每次提前给P申请一片空间,G需要时向P申请,如果空间仍然不够,再向全局通过安全的方式申请空间,此时会多申请一部分空间,以供后面高效的使用。
2.如果M阻塞了,那么直接将P挂载到空闲的M下即可。
三者关系如图:

调度过程
1.如果有新的G生成,就会绑定在一个P下,进行任务的执行。如果P的队列满了,那么进行将G存入一个全局的队列中,等待被抓取,如果有空闲的P,将P和一个活跃的M(go保证至少有一个,成为自旋的M)进行绑定去寻找G任务进行执行。
寻找顺序:
1).自己绑定的P队列
2).全局队列(由于全局队列需要加锁,因此会一次取多个)
3).从其他P队列中偷一半
如果M在执行某个G过程中被阻塞了,会怎么办?
1).将P和其余的G剥离,交给其他的M,继续运行。
2).当从阻塞态恢复后,需要寻找一个P继续执行先前的G进行执行,如果没有空闲的P,就把这个G放入全局队列
如下图:

Go的CSP模型
Go宣扬使用,CSP模型,指要通过通信实现共享,而不是共享实现通信,因此使用管道进行并发控制,管道就是在各个G之间传输数据