Kotlin Coroutine协程源码学习笔记
Kotlin Coroutine协程源码学习笔记
学习kotlin有一段时间了,写个博客记录一下。本文基于kotlin 1.3.31来写,如有任何描述不当的地方,请多多指教纠正。
什么是协程
协程是一种轻量级的线程。那和线程有什么不一样?一.线程的调度是由操作系统负责的,它的睡眠、等待、唤醒都是由操作系统控制,开发者无法决定,而协程则可以由开发者决定代码切换的时机;二.线程要放弃CPU使用时间必须要进入阻塞状态,但阻塞的代价是昂贵的,而相比较而言协程可以通过挂起来切换代码,它相比阻塞而言,代价小很多;三.协程运行在线程中,一个线程中可以包含多个协程。
协程基本使用方法
- 使用GlobalScope.launch、runBlocking之类的协程构建器构建协程。
fun main(){
//启动一个协程并且不会阻塞当前线程
GlobalScope.launch {
println("start a Coroutine in GlobalScope.launch ")
}
//启动一个协程并且阻塞当前线程,直到协程里面所有任务完成
//它被设计作为阻塞代码块和挂起风格的库之间的一个桥梁,因此一般用在main
//函数或者测试代码中,而不应该在协程中启动。
runBlocking {
println("start a Coroutine in runBlocking")
}
}
-
在协程内可使用delay方法或者其他带suspend修饰符的方法表示挂起。带suspend修饰符的方法只能用在协程内,否则会编译报错。
-
可以用一个变量引用该协程返回的值(Job),然后在启动该协程的父协程内的合适的位置调用Job的join方法,那么该作业就会在合适的时机调用。
fun main(){
GlobalScope.launch {
println("start a Coroutine in GlobalScope.launch ")
//开启一个运行在线程池中的协程
val b= async{
println(" async start")
println("async end")
}
println("GlobalScope.launch running")
b.join()
println("end a Coroutine in GlobalScope.launch ")
}
}
//输出结果:
// start a Coroutine in GlobalScope.launch
// GlobalScope.launch running
// async start
// async end
// end a Coroutine in GlobalScope.launch
- 一个协程中使用多个suspend函数时,函数会按顺序执行。假如需要异步并发执行,使用async。
协程源码分析
协程内部是怎么实现的?使用delay之类的挂起函数,协程是怎么挂起以及恢复的?
接下来从一段代码开始分析:
// 新建并启动 blocking 协程,运行在 main 线程上,等待所有子协程运行完成后才会结束
fun main() = runBlocking<Unit> {
println("${Thread.currentThread().name} : runBlocking start")
// 新建并启动 async 协程,运行在 Dispatchers.Default 的线程池中
val b = async(Dispatchers.Default) {
println("${Thread.currentThread().name} : async start")
println("${Thread.currentThread().name} : async end")
}
b.await()
println("${Thread.currentThread().name} : runBlocking end")
}
从runBlocking开始,主要的流程如下:
- 在创建一个协程Coroutine之前,先为它准备一些必要的元素,EventLoop以及CoroutineContext;
- 调用Coroutine.start启动协程;
- 调用Coroutine.joinBlocking循环获取要处理的事件。
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
val currentThread = Thread.currentThread()
val contextInterceptor = context[ContinuationInterceptor]
val eventLoop: EventLoop?
val newContext: CoroutineContext
if (contextInterceptor == null) {
// create or use private event loop if no dispatcher is specified
eventLoop = ThreadLocalEventLoop.eventLoop
newContext = GlobalScope.newCoroutineContext(context + eventLoop)
} else {
// See if cont