协程Coroutines

协程的概念

对操作系统来说,进程是最小的资源管理单元,线程是最小的执行单元。 对于进程和线程,都是有内核进行调度,有CPU时间片的概念,有多种调度算法。

协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以有多个协程。 协程不是被操作系统内核所管理,而是完全在用户态执行,换句话说,协程是用户透明的。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

goroutine和协程的区别

本质上,goroutine就是协程。不同的是,Go在runtime、系统调用等多方面对goroutine调度进行了封装和处理。当遇到长时间执行或进行系统调用时,会主动把当前的goroutine的CPU转让出去,让其他goroutine能被调度和执行,也就是GO从语言层面支持了协程。

在内存消耗方面,每个goroutine默认占用的内存远比C、Java的线程少。 在切换调度开销方面,线程涉及到模式切换(用户态与内核态)、多个寄存器刷新。goroutine只有三个寄存器的值修改(PC/SP/DX)。

协程的底层实现

协程是在应用层模拟线程,他避免了上下文切换的额外消耗,同时兼顾了多线程的优点,简化了高并发程序的复杂性。 实现原理与线程类似,线程a切换到线程b时,需要将线程a的相关执行进度压栈,然后线程b的执行进度出栈,进入线程b的执行序列。协程只不过是在应用层实现了这一点。 协程是基于线程的。内部实现上,维护了一组数据结构和n个线程,真正执行的还是线程。协程执行的代码被扔进一个待执行队列中,由这n个线程从队列中拉出来执行。这就解决了协程的执行问题。那么协程是怎么切换的呢?答案是:golang 对各种 io函数进行了封装,这些封装的函数提供给应用程序使用,而其内部调用了操作系统的异步io函数,当这些异步函数返回busy或bloking时,golang利用这个时机将现有的执行序列压栈,让线程去拉另外一个协程的代码来执行,基本原理就是这样,利用并封装了操作系统的异步函数。 (Go语言运行库封装了异步io,但即使我们通过调整$GOMAXPROCS来充分利用多核CPU并行处理,其效率也不如我们利用io事件驱动设计的、按照事物类型划分好合适比例的线程池,在响应时间上,协作调度是硬伤。)

挖坑:goroutine的详细底层实现和应用

应用