多内核线程+多协程,以及Golang的GMP模型

2,357 阅读4分钟

本人做了一个动态流程图的网页,能像ppt动画一样,通过不断点击出现流程图的每一项,从而达到动态。由于ui用的是drawio的ui,所以该动态流程图既美观,而且方便制作流程图,然后又能实现ppt通过点击出现元素的功能。

下图的图解的动态流程图地址:(出现元素后,需要等待两秒,元素消失后,即可通过不断点击出现元素)

http://43.138.251.145:2335/drawio/Golang%E7%9A%84GMP.html

用户级上下文:  

寄存器上下文:此刻的各种寄存器的数据,比如pc寄存器保存着下一条要执行的指令。

系统级上下文:进程控制块、内存信息等

IO操作:内存、磁盘、网络、外设的输入和输出,耗时却不需要cpu

线程切换:

线程切换只涉及寄存器上下文。

当在单核cpu上运行的线程三切换到线程一时:
需要保存线程三此刻的上下文(比如程序运行到哪一条语句),
然后加载上一次运行线程一的上下文,执行线程一的任务

最终目的:榨干cpu的计算时间,为此,不能把这时间浪费在io操作、线程切换、空闲。

cpu单核:计算用

并行编程:利用多个cpu单核执行多个任务

并发编程:在单核(伪)上,运行多个任务。如果是用户态线程就是单核、如果是内核态线程就是多核

并发编程的作用:我们只讨论->为了不将cpu时间浪费在io操作

多线程:

问题**:**如果此刻线程三的任务很紧急怎么办,那么操作系统会把线程三放入队列的第一位,这样线程三就能提前执行任务

抢占式调度**:**操作系统会根据每个线程的优先级进行调度,决定他们的执行顺序。

多线程的优点:将主要进行io操作的线程(比如读取硬盘数据)的优先级调低,不会被io操作浪费大量的cpu时间

多线程的缺点:线程切换需要保存上下文,需要时间。

协程:用户态的轻量级的非抢占线程,协是协作的意思,即相互协作完成任务的协程们

// 伪代码 
// 监听80端口,为每一个请求运行returnHtml(),即生成一个协程  
function listener(){        
    // 监听80端口        
    returnHtml()  
}
// go代表以协程的方式运行该函数  
go function returnHtml(){       
    // 读取磁盘中的html文件        
    // 挂起自身对应的协程,让出线程给别的协程        
    // 读取完毕,返回html文件给用户  
}

用户态:协程之间的切换是用户写的代码决定的。

轻量级:因为不存在线程的切换,而且某个协程的上下文
保存在该协程上,所以切换协程消耗很小。

非抢占:协程之间的切换不是通过优先级的抢占,而是用户决定的。

协程的缺点:只能在一个线程上运行,所以只能利用一个单核cpu。

协程解决了什么问题:

  1. 比线程切换耗时少

  2. 多线程存在的问题,多协程不存在,比如共享的资源。

问题:在使用协程的基础上,如何利用多核cpu?问题:在使用协程的基础上,如何利用多核cpu?

方案:多内核态线程+多协程

问题:由于协程的调用是用户写的代码决定的,
但是内核态线程是操作系统进行调度的。
所以这个方案本身还是要涉及共享资源的问题。
这无疑给用户写代码时,对调度的设计增加了难度。
但是对于大量网络io的应用程序,调度的复杂度却不高。

Golang的核心:GMP模型 --> www.zhihu.com/question/20…

接下来讲解两种GoLang能更有效利用cpu时间的情况:

若此时M0内核线程被操作系统中断,那么p便会移动到空闲的内核态线程,比如M1。

若此时M1的出于空闲状态,那么新的p调度器便会去偷别处p的G,然后挂载在空闲的M1上,这样cpu利用率更高。

最终目的:榨干cpu的计算时间,为此,不能把这时间浪费在io操作、线程切换以及空闲。