05-协程
2.2.3 协程
Kotlin 1.1带来的关键特性之一就是协程,它带来了async/await、 yield 以及类似的编程风格。对于一些耗时的操作(如CPU或GPU密集型任务等),协程提供一种既可避免阻塞又廉价可控的操作方式:协程挂起(coroutine suspension)。协程将复杂的异步操作放入底层库中,程序逻辑可顺序表达,以此来简化异步编程。该底层库可以将用户的代码包装为回调/订阅事件,在不同线程(甚至不同机器)上调度执行。
和线程/进程相比,协程是通过编译技术来实现的,不需要虚拟机VM和操作系统OS的支持。更重要的是,协程挂起可由用户来控制,也无须上下文切换和OS操作。而协程和线程/进程另一个不同的地方是,协程不能在随机指令中挂起,而只能在挂起点(即调用标记函数)挂起。
下面是kotlinx.coroutines 外部库中 async/await的部分实现代码。
// 在后台线程池中运行代码
fun asyncOverlay() = async(CommonPool) {
// 启动两个异步操作
val original = asyncLoadImage("original")
val overlay = asyncLoadImage("overlay")
// 然后应用叠加到两个结果
applyOverlay(original.await(), overlay.await())
}
// 在页面上下文中启动新的协程
launch(UI) {
// 等待异步叠加完成
val image = asyncOverlay().await()
// 然后在页面中显示
showImage(image)
}
在上面的代码中,async函数启动一个协程。当调用asyncOverlay().await()时,挂起执行的协程,转而执行正在等待的操作,在操作完成后协程恢复。
除此之外,通过对yield和yieldAll函数的使用还可以支持惰性生成序列。在生成的序列中,每个元素在被取回前挂起代码块,并在下一次元素请求时恢复。下面是使用yieldAll 函数生成序列的例子。
import kotlin.coroutines.experimental.*
fun main(args: Array<String>) {
val seq = buildSequence {
for (i in 1..4) {
// 产生一个 i 的平方
yield(i * i)
}
// 产生一个区间
yieldAll(26..28)
}
// 输出该序列
println(seq.toList())
}
上面的例子输出结果如下。
[1, 4, 9, 16, 26, 27, 28]
协程是Kotlin的重要特性之一,本节只是粗略地介绍了协程相关的知识,后面的章节会重点介绍。