协程是通过编译技术实现(不需要虚拟机VM/操作系统OS的支持),通过插入相关代码来生效! 与之相反,线程/进程是需要虚拟机VM/操作系统OS的支持,通过调度CPU执行生效!
Kotlin Coroutines(协程) 完全解析(一),协程简介
Kotlin Coroutines(协程) 完全解析(二),深入理解协程的挂起、恢复与调度
Kotlin Coroutines(协程) 完全解析(三),封装异步回调、协程间关系及协程的取消
Kotlin Coroutines(协程) 完全解析(四),协程的异常处理
Kotlin Coroutines(协程) 完全解析(五),协程的并发
英文:
https://kotlinlang.org/docs/reference/coroutines/basics.html
https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutines-guide.md
https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md
中文:
https://www.kotlincn.net/docs/reference/coroutines/coroutines-guide.html
https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope
https://developer.android.google.cn/kotlin/coroutines
异步的含义是被调用的方法执行完之后,无法直接拿到返回值(切换了线程),需要通过回调接收返回值
协程相当于提前切换到子线程,然后同步走逻辑,进而改变每一层的异步调用为同步,
协程将同步方法和线程切换两者相隔离,所有方法都是同步方法,单独控制执行线程,避免异步方法去接收回调参数
区别:
fun requestTokenAsync(cb: (Token) -> Unit) { ... }
fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
requestTokenAsync { token ->
createPostAsync(token, item) { post ->
processPost(post)
}
}
}
fun requestTokenAsync(): CompletableFuture<Token> { ... }
fun createPostAsync(token: Token, item: Item): CompletableFuture<Post> { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }
.exceptionally { e ->
e.printStackTrace()
null
}
}
fun requestToken(): Token { ... }
fun createPost(token: Token, item: Item): Post { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
Single.fromCallable { requestToken() }
.map { token -> createPost(token, item) }
.subscribe(
{ post -> processPost(post) }, // onSuccess
{ e -> e.printStackTrace() } // onError
)
}
suspend fun requestToken(): Token { ... } // 挂起函数
suspend fun createPost(token: Token, item: Item): Post { ... } // 挂起函数
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
GlobalScope.launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
// 需要异常处理,直接加上 try/catch 语句即可
}
}
协程的挂起通过suspend
挂起函数实现,协程的恢复通过Continuation.resumeWith
实现。
suspend fun requestToken(): Token { ... }
实际上在 JVM 中更像下面这样:
Object requestToken(Continuation<Token> cont) { ... }
Continuation
的定义如下,类似于一个通用的回调接口:
/**
* Interface representing a continuation after a suspension point that returns value of type `T`.
*/
public interface Continuation<in T> {
/**
* Context of the coroutine that corresponds to this continuation.
*/
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing successful or failed [result] as the
* return value of the last suspension point.
*/
public fun resumeWith(result: Result<T>)
}
现在再看之前postItem
函数:
suspend fun requestToken(): Token { ... } // 挂起函数
suspend fun createPost(token: Token, item: Item): Post { ... } // 挂起函数
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
GlobalScope.launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
然而,协程内部实现不是使用普通回调的形式,而是使用状态机来处理不同的挂起点,大致的 CPS(Continuation Passing Style) 代码为:
// 编译后生成的内部类大致如下
final class postItem$1 extends SuspendLambda ... {
public final Object invokeSuspend(Object result) {
...
switch (this.label) {
case 0:
this.label = 1;
token = requestToken(this)
break;
case 1:
this.label = 2;
Token token = result;
post = createPost(token, this.item, this)
break;
case 2:
Post post = result;
processPost(post)
break;
}
}
}
上面代码中每一个挂起点和初始挂起点对应的 Continuation 都会转化为一种状态,协程恢复只是跳转到下一种状态中。挂起函数将执行过程分为多个 Continuation 片段,并且利用状态机的方式保证各个片段是顺序执行的。
cancel()
或者异常结束,会立即取消它的所有子协程