若是你对如下几个问题有疑问,那么本文可能会有所帮助。html
1.2.3
linux
谈协程绕不开线程,按传统还得从进程谈起,不过我想业内人员对进程和线程应该是耳熟能详,这里就简单归纳下。git
进程拥有本身独立的堆和栈,既不共享堆,亦不共享栈,进程由操做系统调度;线程拥有本身独立的栈,共享堆(也能够有本身的私有域),不共享栈,线程亦由操做系统调度。一个进程能够有多个线程。github
多线程一直以来是面试必考点,虽然[web]服务端开发人员彷佛历来不用直接操做线程,实际上是由于框架帮忙维护了,开发人员只须要关心业务实现。这也致使了部分人对多线程的某些概念模糊不清。好比关于多线程的效率:在多核cpu下,多个线程能够并行运行在不一样内核上,效率高;而在单核cpu中,多个线程的并行执行实际上是一个错觉,由于它们都是运行在一个内核上,一个cpu内核同一时间只能执行一个进程/线程,所以在一个内核上的多线程执行其实效率反而比串行执行低,只是给用户一种并发的错觉,反而增长了线程切换的时间。web
可是效率的高低还要看线程占用cpu资源的占用率,好比存在大量IO操做,IO比较慢。也就是说,若是只有单线程,那么一旦涉及到IO操做,线程可能会被阻塞,程序的其他逻辑就只能傻等,就算那些逻辑不依赖于这个IO操做,此时线程对CPU的使用为0,CPU就是空闲状态。若是是多线程,是线程瓶颈,那么其他线程则可使用cpu,而非等待IO结束。面试
题外话,一个空循环就能让cpu满载,参看 为何一个空的死循环会让CPU占用达到100%。算法
后来,出现了多路复用之类的技术,原先须要等待IO返回的线程也不须要等了,能够和其它线程同样忙别的事,IO返回时获得通知再处理接下去的事情。Java的NIO和.Net的async/await就是这么干的。编程
通常来讲,为了不线程频繁建立销毁带来的性能问题,程序里都会使用到线程池。windows
然而仍是在单核的场景下,事情彷佛变得有点诡异。既然线程们如今都能心无旁骛地使用CPU计算,而前面也说了,一个cpu内核同时只能运行一个线程,管理多线程又是抢占式,又是栈切换,维护生命周期啥的,影响性能不说,彻底没得必要嘛,为何不仅用一个线程完成全部的计算呢。什么,你说可能须要[伪]并行计算?那就让线程本身来安排咯,毕竟具体逻辑方面,线程自己(或者说开发人员)比CPU要清楚的多,知道何时该干什么,何时切换逻辑,何时不切换,都由线程本身说了算。因而,协程粉墨登场。api
协程主要是针对单线程的一个概念(如Js、NodeJs、Python因为GIL致使的伪多线程),能够将其看做线程运行时片断。和线程相似,虽然貌似多个协程能够并行执行,一个时间仍然只能运行一个。因此,若是业务逻辑是顺序相关(串行)或者各任务对反馈及时性要求不高,那么不必用协程,就跟不必多线程同样。协程对比线程,除了有更好的性能外,还让开发人员对执行片断有了更好的掌控。好比Go语言,经过阻塞条件(time.sleep()、select{}等),咱们能够手动将控制权转移给其它的 Go 协程 , 也能够说是告诉调度器让它去调度其它可用空闲的 Go 协程(Go如何判断这是阻塞代码还没有研究过);或者经过channel调度指定协程。
Go默认状况下只用单线程。这就是说,你即便开了几百个goroutine,系统中同一时间在跑的只有一个线程,也就是一个协程。依据上面的内容,你们能够思考下Go为什么默认如此。咱们能够经过 runtime.GOMAXPROCS() 设置的是Go语言能跑几个线程,讲道理,CPU几核跑几个线程比较合理,使用 runtime.NumCPU() 查看内核数。
在编程层面来讲,协程的概念偏向于以同步编程的模式实现异步处理的编程模式,避免了多层回调代码嵌套的问题。
其实在不少年之前,协程已经被提出了,如今只是它焕发生机的阶段。
4
上文说了,协程之间应该是非顺序相关的,即它们的上下文没有强依赖关系,是相对独立的。这里的上下文指的就是当前的运行栈空间,它包括了参数、局部变量、各寄存器的值等内容。在协程切换的时候,咱们要想办法将对应的上下文投射到当前线程的运行栈中,即让线程执行特定的上下文。很容易想到malloc一块临时内存存放挂起的协程上下文信息,resume的时候再覆盖回去,运行栈在内存中只有一处,这就是stackless模式。相对的还有stackful模式,在这种模式下,每一个协程都有本身的栈空间,运行栈指的就是当前协程的栈空间。现有语言的实现中,Python, Kotlin等定义的就是stackless协程, Go语言中实现的是stackful协程。
对于其它没有在语言层面直接支持协程的语言来讲,因为协程涉及到底层的[堆]栈切换控制,所以很难单纯依靠现有语法构建算法的方式实现。有人作过此类尝试(可参看Coroutines in C),但也没有实用性。
能直接操做执行堆栈并暴露api的,如今市面上的语言以C/C++最为流行,基于它们也有不少开源的协程库。下面介绍几种实现方式。
协程分为非对称协程和对称协程。在非对称协程中,调用者和被调用者的关系是固定的,调用者将控制流转到被调用者,被调用者运行完毕后只能返回到调用者,而不能返回到其余协程。对称协程则否则。对称协程能够很容易由非对称协程来表达。且按通常的调用逻辑,A调B,B应返回到A,再由A发起到C的调用,而非B直接返回到C。所以,目前大多数协程库都只实现非对称协程。
关于汇编语法的平台差别,类Unix下采用的是AT&T的汇编语法格式,Dos/Windows下面采用的是Intel汇编语法格式。
参考资料:
转载请注明本文出处:http://www.javashuo.com/article/p-hcsueoiu-gh.html