阿拉丁和灯

Thoughts, stories and ideas.



Kotlin Coroutines 释疑

背景

异步和并发,作为现在编程的核心问题之一,已经成为编程语言各显神通的地方。这方面的特性,可以从两个重要的方面来看:

  1. 外在表现形式(语法关键字,API)
  2. 内在的实现(用线程,还是其他)

Kotlin在这两方面都有创新,在借鉴其他语言的经验的基础上,取其优点,加上独特的改进,形成了自己的Coroutine实现和相应的suspend关键字及coroutine API。深入了解了Kotlin coroutine之后,你会感觉到它的设计非常精彩。但是,里面涉及到一些与我们的日常编程直觉不那么一样的地方,需要花一点时间仔细梳理理解一下,理解之后,一切就会变得非常自然和易于理解,便于使用了。

外在形式

事实

  • suspend 关键字标明 suspend function, suspend function只能被另一个suspend function直接调用,或者在一个coroutine 里(由coroutine builder创建;本质是具有一个有效的coroutine context)被调用

  • Coroutine builders: 三大金刚runBlocking, launch, async。runBlocking最简单,启动之后就等待,

Builder 是否suspend(传播异步) 是否返回结果 返回Defered还是基本类型
runBlocking 基本类型
launch N/A
async Defered
await 基本类型(T)

除此之外还有future,跟async的作用类似,只不过是用于Java互操作,产生的是Java CompletableFuture。可以在产生出来的东西上调用await()。

  • Defered 继承自 Job

  • delay是一个suspend function,意味着它会提前返回,到时间后再继续执行后面的代码。

概念

Java 8中的CompletableFuture是传播的,体现了“异步”这种东西的传播性的本质。而这种本质在Kotlin Coroutine里面用一种不同的形式体现了,这就是suspend关键字,所有带suspend关键字的函数,都类似于CompletableFuture。比如delay(), await()。

yield是异步性的引入者,在Java里面,异步是由线程来支撑的(作为背后的实现),而在Kotlin里面,异步是由Coroutine来支撑的,也就是说,yield将会创建一个Coroutine。yield所创建的异步性,由suspend关键字传播出去。

async, launch,runBlocking,这些是异步性的处理者,需要注意的是,在Kotlin当中,对于异步性的默认处理是同步调用,如果需要让异步性发挥效力,要用async函数来调用。调用得到的是Defered

await所起的作用类似于Java 8 CompletableFuture上的thenApply等方法所起的作用,就是维持住异步性的同时,让处理代码可以接收一个一般类型,而不是Defered类型,像写正常业务逻辑一样,不用考虑异步性。

API实现

  1. yield

看一下yield的实现:

image.png

可以看到,里面调用了suspendCoroutineOrReturn函数,而suspendCoroutineOrReturn函数又做了什么呢?
image.png

可以看到,这个方法调用了suspendCoroutineUninterceptedOrReturn,而这个suspendCoroutineUninterceptedOrReturn已经是内部代码了,在类库代码里面已经不可见了,也就是说,到这里就深入了Kotlin Coroutine的实现内部,我们可以知道,这里就是创建协程(Coroutine)的地方了。

  1. async

image.png

  1. await

image.png

  1. runBlocking

image.png

  1. launch

image.png

相关文件

image.png

所在路径

image.png

内在实现

Coroutine API背后使用的是coroutine来实现,用的是基于传播continuation(改良版的callback)的实现。


Jacob Wu