在CPU调度中,为了解决阻塞的问题,操作系统具备了并发能力:当一个进程阻塞的时候,切换到另外一个等待执行的进程,这样就尽量把CPU利用了起来,CPU资源尽可能得到利用。

操作系统以分配时间片的方式达到并发功能。


但新的问题就又出现了,进程的创建、切换、销毁、都需要占用CPU资源,如果进程太多,CPU有很大一部分资源都用来进行调度了。


多进程/多线程提高了系统的并发能力,但是为每个任务都创建线程是不现实的,因为会导致消耗大量内存。

所以大量的线程,进程存在两个问题:

1、高内存占用

2、调度消耗CPU


Go语言开发者为了解决这个问题,将线程分为了“内核态”线程和“用户态”线程。

“用户态”线程必须绑定“内核态”线程,CPU并不知道有“用户态”线程的存在,它只知道它运行的是一个“内核态”的线程。


我们再细分分类一下,内核线程依然叫“线程”,“用户态”线程叫做“协程”。


看到这里,我们不妨使多个协程绑定一个线程,那么我们就可以看到有三种协程与线程的映射关系了

N:1关系

N个协程绑定1个线程,优点就是协程在用户态线程即完成切换,不会陷入到内核态,这种切换非常的轻量快速。但也有很大的缺点,1个进程的所有协程都绑定在1个线程上


缺点:

  • 某个程序用不了硬件的多核加速能力

  • 一旦某协程阻塞,造成线程阻塞,本进程的其他协程都无法执行了,根本就没有并发的能力了。

1:1关系

一个协程绑定一个线程,这种最容易实现。协程调度都有CPU完成了,不存在N:1的缺点

缺点

  • 协程的创建、删除、切换都由CPU完成,资源占用率高。

M:N关系

M个协程绑定1个线程,是N:1和1:1类型的结合,克服了以上2种模型的缺点,但实现起来最为负载。

Go语言中,协程被称为goroutine,对比线程非常轻量,一个goroutine之占几KB,并且这几KB足够goroutine运行完,这就能在有限的内存空间支持大量goroutine,虽然一个goroutine栈只占用几KB,单实际上如果需要更多的内存空间,rentime会为goroutine分配。

本章主要讲的是Go语言协程的调度器的运行场景,下一章我们将深入了解调度器的运行机制,更深入了解GMP设计思想。



我们是程序员khaos,一群酷爱编程,乐于分享的小伙子,下期见~



「分享」「点赞」「在看」是最大支持