本文介绍一些并发的基础知识、常见的并发模型一级Go语言的MPG并发模型及其运行原理
并发与并行的区别
- | 并发 | 并行 |
---|---|---|
概念 | 并发指同一时间段,多条命令在CPU上同时执行。 | 并行指同一时刻,多条命令在CPU上执行 |
运行原理 | 并发程序不要求计算机有多核计算能力,在同一时段内,多个线程会被分配到一定的执行时间片,在CPU轮训执行。线程执行时间到或者执行完毕,会被CPU调度换下 | 并行程序要求计算机有多核计算能力,同一时刻多个线程CPU的的多个内核上执行命令。 |
其他 | 可能会有线程互相等待、变为串行。 | 完全独立,不需要等待 |
GO语言中的并发模型
1)线程与锁并发模型:基于共享内存实现,依赖开发人员的能力和技巧,不容易排查
2)CSP并发模型:通讯手段共享内存,并发实体是独立的,通过通道实现数据交互,通道读取数据和存放数据会阻塞并发实体,容易造成死锁
线程模型
操作系统分为 内核空间和用户空间。线程分为内核线程和用户线程。
1)用户线程调度由线程完成,无需切换内核、资源消耗少且高效。同一进程下创建的用户线程是以进程维度参与CPU的竞争,用户线程分时复用分配CPU时间
2)内核线程调度由操作系统完成,切换时CPU需要切换到内核态,通过系统调用使用内核线程。
常见的线程模型
1)用户级线程模型:一个进程对应一个内核线程。调度由用户完成,实现与编程语言,使用分时复用调度,非常轻量级高效, 但无法利用多核优势,容易发生阻塞。
2)内核级线程:进程的每个线程都会对应一个内核线程。调度由系统完成,可以利用多核优势,线程之间不会发生阻塞。但上下文切换时会从用户态切换到内核态,有资源消耗,创建的线程数依赖操作系统内核线程数量。
3)两级线程模型:一个进程对应多个内核线程,由进程的调度器决定如何分配内核线程,进程预先申请一定数量的内核线程,线程的调用由调度器负责,内核线程的调度由操作系统负责,降低线程创建的资源消耗,单增加了切换上下文处理和栈大小管理等
go的MPG线程模型
1)Machine:一个Machine对应一个内核进程,在M的生命周期内,M和内核的绑定关系不变
2)Processor:执行必须的上下文环境,用户代码逻辑处理器,运行时一个M只能绑定一个P,M和P的组合才可以运行GO程序
3)goroutine:用户代码片,用户线程,P和义绑定多个G,多个G排成队列挂载P上面,依次调度。
a)当没有足够的M和P组合时,会创建新的M,单个程序中M的数量往往大于P,P由程序决定,P的最大数量决定了并发规模通过环境变量GOMAXPROCS或者函数runtime.GOMAXPROCS修改配置
b)当G0因为某些原因阻塞了M,P会携带剩余的G投入其他空闲的M中,如果没有空闲的会新建一个M。当M对应的内核线程被唤醒时,M会为G0捕捉一个空闲的P,如果不成功,就会把G0放入可执行的G队列后面,等待其他P查找。
d)为了保证G的均衡执行,非空闲的P执行完成后,会从可执行G的队列中获取待执行的G,也可能会从其他P的G队列中掠夺G