一、CSP并发模型
1、CSP是什么?
CSP是Go的一种并发模型,是通过Goroutine和channel实现的,用于描述两个独立的并发实体(Goroutine)通过共享的通讯 channel(管道)进行通信的并发模型。
2、核心思想
以通信的方式来共享内存,不要以共享内存的方式来通信
CSP的核心观念是将两个并发执行的实体通过通道channel连接起来,所有的消息都通过channel传输。
二、GMP
1、GMP的前身GM
G:G就是我们通过go关键字开启的Goroutine
M:M是内核线程
除了G和M,还会存在一个全局的队列,这个全局队列可以存放多个处于可运行状态的G,M如果想要获取G就需要访问全局队列。
2、GM的问题
从表面来看,貌似GM就已经挺好的了,那么为什么会出现GMP呢?
①存在单一全局锁
内核线程可以同时存在多个,为了线程安全,全局队列会有一个锁,每次访问都需要先获取这个锁,锁竞争严重。
②频繁的线程阻塞和解阻塞
在存在 syscalls 的情况下,线程经常被阻塞和解阻塞。这增加了很多额外的性能开销。
③Goroutine的传递
G交接下一个G,线程之间会经常交接可运行的Groutine,会导致延迟增加和额外的开销。
3、GMP
G和M我们前面已经提到了,那么P是什么呢?
P:处理器,负责G与M的连接,它能提供线程所需要的上下文环境,也能分配G到它应该去的线程上执行
P的加入带来了一个本地队列,和全局队列类似,也是用于存放G的,想要获取等待运行的G,需要优先从本地队列获取,访问本地队列无需加锁。虽然我们有了本地队列,但是全局队列仍然是存在的
。
GM模型里M想要运行G,直接去全局队列里拿就行了;GMP模型里,M想要运行G,就得先获取P,然后从 P 的本地队列获取 G。
新建 G 时,新G会优先加入到 P 的本地队列;如果本地队列满了,则会把本地队列中一半的 G 移动到全局队列
。
P的本地队列为空时,就从全局队列里去取。如果全局队列也为空,则会发生work-stealing,M会从其他P的本地队列中偷取G
如果G发生阻塞,那么M会寻找其他G来执行
如果G发生系统调用,那么M也会进入系统调用状态,而P就会寻找其他空闲的M
4、GMP的数量限制?
G:没有数量限制,理论上和内存有关,创建一个Goroutine需要2~4K的连续内存
M:默认限制数量是10000,可调整
P:受本机核数影响,可调整