协程的概念
最早的协程概念出自1963年.经历多代发展.现在支持协程的编程语言有:Golang、Kotlin、C#、JS、Python等.
协程本来是指由编程语言自身负责调度任务,不借助操作系统与处理器功能的任务调度方式。本质就是非抢占式多任务,它当且仅当任务主动让出控制权时进行调度。这种调度从操作系统看起来其实就是同一个线程,只不过内部实现了非抢占式多任务,即:同时只能运行一个协程,其它协程只能等待,当前协程需要等待的时候主动把控制权让出来,让其它协程工作,这样就可以允许同一个线程内跑多个协程。
按调用栈分
协程分为有栈stackful和无栈stackless
Golang里的goroutine是有栈;JavaScript,Kotlin async/await是无栈
两者的含义不是指协程在运行时是否需要栈,一个函数调用另一个函数,总是存在调用栈的;而是指协程是否可以在其任意嵌套函数中被挂起.有栈协程是可以的,而无栈协程不可以
简要概括:
有栈协程(Stackful Coroutine): 每一个协程都有自己的调用栈,类似于线程的调用栈
无栈协程(Stackless Coroutine): 协程没有自己的调用栈,挂起点的状态由状态机或者闭包等语法来实现
优势比较:
有栈协程: 可以在任意函数调用层级的任意位置挂起,并转移调度权
无栈协程: 是不需要开辟栈空间,因此在内存紧张的程序上有优势
按调度方式分
协程分为对称和非对称
在协程调度过程中,根据协程调度权的转移目标的不同区分
简要概括:
对称协程(Symmetric Coroutine):任何一个协程都是相互独立且平等的,调度权可以在任意协程之间转移
非对称协程(Asymmetric Coroutine):协程出让调度权的目标只能是它的的调用者,即协程之间存在调用和被调用关系
kotlin协程
Kotlin协程常被认为是伪协程,是因为它并不在同一个线程运行,而是真的会创建多个线程,如果你创建了多个协程会发现程序实际运行在多个线程而不是一个线程上,这破坏了协程的定义.
其实这和协程的作用域有关.
总结来说:
- 一个线程一次只能运行一个协程
- 一个线程可以同时处理多个协程
它可以挂起一个协程并运行一个不同的协程,但是在给定的时间点上,一个线程上只有一个协程在运行,不能在同一时间点在同一线程上运行多个协程
挂起的本质
协程挂起的是协程本身,协程可以看作是线程运行的指令序列,直到它遇到一个挂起点,在这个点上,协程挂起它的执行(保存调用堆栈和本地变量以便稍后恢复),并产生控制,在这种情况下,它不再在它运行的线程上运行.
通俗点解释就是: 从哪里挂起?从当前线程挂起,就是这个协程从正在执行它的线程上脱离.
挂起会让协程从正在执行它的线程上脱离
具体到代码其实是:
协程的代码块中,线程执行到了suspend 函数的时候,就暂时不再执行剩余的协程代码,跳出协程的代码块
那线程接下来会做什么呢?
如果它是一个后台线程:或者什么都不做,或者执行别的后台任务.
协程在执行到有 suspend 标记的函数的时候,会被suspend(被挂起),就是切个线程;挂起函数在执行完成之后,协程会重新切回它原先的线程,在 Kotlin 中的挂起,就是一个稍后会被自动切回来的线程调度操作