Kotlin协程源码分析-5 状态机
上文链接:Kotlin协程源码分析-4 状态机在上文中最后一行 “挂起函数的Continuation对象如何才能获取?”。“如何返回一个挂起标识符COROUTINE_SUSPENDED”。本文将做就这两个问题详细介绍获取挂起函数的Continuation对象你可以直接看kotlin协程库实现方式得到答案。这里直接给出答案。suspend fun mySuspendOne() = susp...
上文链接:Kotlin协程源码分析-4 状态机
在上文中最后一行 “挂起函数的Continuation对象如何才能获取?”。“如何返回一个挂起标识符COROUTINE_SUSPENDED”。本文将做就这两个问题详细介绍
获取挂起函数的Continuation对象
你可以直接看kotlin协程库实现方式得到答案。这里直接给出答案。
suspend fun mySuspendOne() = suspendCoroutineUninterceptedOrReturn<String> { continuation ->
//传入的continuation就是SuspendLambda对象,也就是我们自己写一个lambda编译后的对象
//启动一个线程。一秒后回调continuation函数
thread {
TimeUnit.SECONDS.sleep(1)
logD("我将调用Continuation返回一个数据")
//调用又会重新调用BaseContinuationImpl的resumeWith函数
continuation.resume("hello world")
}
logD("mySuspendOne 函数返回")
//因为我们这个函数没有返回正确结果,所以必须返回一个挂起标识,否则BaseContinuationImpl会认为完成了任务。
// 并且我们的线程又在运行没有取消,这将很多意想不到的结果
kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}
上面的代码会在编译后变成如下:
上面其实很危险,如果我们函数忘记写返回COROUTINE_SUSPENDED那么默认返回Unit,BaseContinuationImpl在resumeWith判断不是挂起标识,那么会认为完成了任务,此时会回调监听的完成的Continuation对象.这自然会引起很多意外的现象.Kotlin考虑到这种情况下推荐我们使用如下的方法获取当前continuation
suspend fun mySafeSuspendOne() = suspendCoroutine<String> { continuation ->
//传入的continuation就是SuspendLambda对象,也就是我们自己写一个lambda编译后的对象
//启动一个线程。一秒后回调continuation函数
thread {
TimeUnit.SECONDS.sleep(1)
logD("我将调用Continuation返回一个数据")
//调用又会重新调用BaseContinuationImpl的resumeWith函数
continuation.resume("hello world")
}
logD("mySuspendOne 函数返回")
//suspendCoroutine函数很聪明的帮我们判断返回结果如果不是想要的对象,自动返回kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}
我们简单看下suspendCoroutine实现
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =
suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
//封装一个代理Continuation对象
val safe = SafeContinuation(c.intercepted())
block(safe)
//根据block返回结果判断要不要返回COROUTINE_SUSPENDED
safe.getOrThrow()
}
这里先跳过SafeContinuation代理对象的分析。 简单理解就是帮你判断是不是要返回COROUTINE_SUSPENDED(当然这个SafeContinuation还有栈相关的内容)。
我们来一个案例
//com.example.studycoroutine.chapter.five.FiveDemo.kt
suspend fun mySuspendOne() = suspendCoroutineUninterceptedOrReturn<String> { continuation ->
//传入的continuation就是SuspendLambda对象,也就是我们自己写一个lambda编译后的对象
//启动一个线程。一秒后回调continuation函数
thread {
TimeUnit.SECONDS.sleep(1)
logD("我将调用Continuation返回一个数据")
//调用又会重新调用BaseContinuationImpl的resumeWith函数
continuation.resume("hello world")
}
logD("mySuspendOne 函数返回")
//因为我们这个函数没有返回正确结果,所以必须返回一个挂起标识,否则BaseContinuationImpl会认为完成了任务。
// 并且我们的线程又在运行没有取消,这将很多意想不到的结果
kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}
suspend fun mySafeSuspendOne() = suspendCoroutine<String> { continuation ->
//传入的continuation就是SuspendLambda对象,也就是我们自己写一个lambda编译后的对象
//启动一个线程。一秒后回调continuation函数
thread {
TimeUnit.SECONDS.sleep(1)
logD("我将调用Continuation返回一个数据")
//调用又会重新调用BaseContinuationImpl的resumeWith函数
continuation.resume("hello world ")
}
logD("mySuspendOne 函数返回")
//suspendCoroutine函数很聪明的帮我们判断返回结果如果不是想要的对象,自动返回kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}
fun testFiveOne() {
val myCoroutine = MyCoroutine()
val mystartSuspend: suspend () -> String = {
mySuspendOne()
mySafeSuspendOne()
}
//启动协程
mystartSuspend.startCoroutine(myCoroutine)
logD("启动协程,由于两个挂起函数延迟返回结果所以这句话会先打出来")
}
输出:
[main] mySuspendOne 函数返回
[main] 启动协程,由于两个挂起函数所以这句话会先打出来
[Thread-2] 我将调用Continuation返回一个数据
[Thread-2] mySuspendOne 函数返回
[Thread-3] 我将调用Continuation返回一个数据
[Thread-3] MyCoroutine 回调resumeWith 返回的结果 hello world
对于上面的输出顺序读者必须弄清,弄清的时候等于弄清Kotlin协程核心编程思想。
那么这边手动梳理下流程:
首先看下编译后mystartSuspend对象:
static final class MystartSuspend extends SuspendLambda implements Function1 {
int label;
Object result;
public final Object invokeSuspend(Object $result) {
Object SUSPEND_FLAG = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int current_statue= this.label;
if(current_statue== 0) {
ResultKt.throwOnFailure($result);
this.label = 1;
//(一)此处返回SUSPEND_FLAG,等mySuspendOne调用传入Continuation对象
//重新调用到invokeSuspend函数
if(FiveDemoKt.mySuspendOne(((Continuation)this)) == SUSPEND_FLAG) {
//(二) 相等所以返回挂起标识给父类结束本次调用。
return SUSPEND_FLAG;
}
label_21:
this.label = 2;
//(三) mySuspendOne调用传入Continuation的resume函数回调这
result = FiveDemoKt.mySafeSuspendOne(((Continuation)this));
//(四) 此处同样返回挂起标识(一个挂起函数没有立即返回结果那么必然要返回SUSPEND_FLAG)
if(result == SUSPEND_FLAG) {
//(五)
return SUSPEND_FLAG;
}
}
else {
if(current_statue!= 1) {
if(current_statue== 2) {
//(六) 最后返回结果,mySafeSuspendOne回调Continuation的resume的回到这
ResultKt.throwOnFailure($result);
return $result;
}
throw new IllegalStateException("call to \'resume\' before \'invoke\' with coroutine");
}
ResultKt.throwOnFailure($result);
goto label_21;
}
return result;
}
}
上图吧:
















那么看完上面的图示,我相信你应该明白所谓kotlin状态机就是接口回调罢了。明白状态机后面在学习kotlin协程就像玩一样。
下回分析kotlin上下文
更多推荐



所有评论(0)