You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/* * * [DEFAULT] -- immediately schedules coroutine for execution according to its context; * * [LAZY] -- starts coroutine lazily, only when it is needed; * * [ATOMIC] -- atomically (non-cancellably) schedules coroutine for execution according to its context; * * [UNDISPATCHED] -- immediately executes coroutine until its first suspension point _in the current thread_.*/
internalfun <T> Continuation<T>.resumeCancellable(value:T) =when (this) {
isDispatchedContinuation-> resumeCancellable(value)
else-> resume(value)
}
@Suppress("NOTHING_TO_INLINE") // we need it inline to save us an entry on the stackinlinefunresumeCancellable(value:T) {
val context = continuation.context
if (dispatcher.isDispatchNeeded(context))
dispatcher.dispatch(context, DispatchTask(continuation, value, exception =false, cancellable =true))
else
resumeUndispatched(value)
}
前言
本文主要介绍一下Kotlin是如何实现Coroutine的,对于具体的用法推荐参考一下官方文档,讲得还是比较详细的
什么是 Coroutine
概念上来说类似于线程,拥有自己的栈、本地变量、指令指针等,需要一段代码块来运行并且拥有类似的生命周期。但是和线程不同,coroutine并不和某一个特定的线程绑定,它可以在线程A中执行,并在某一个时刻暂停(suspend),等下次调度到恢复执行的时候在线程B中执行。不同于线程,coroutine是协作式的,即子程序可以通过在函数中有不同的入口点来实现暂停、恢复,从而让出线程资源。
实战演练
首先看一个简单的小demo,来看看Kotlin的Coroutine是具体适合使用的:
上面这段代码会输出
Hello World!
, 那么下面我们看看具体是如何工作的.原理剖析
asyn()这里是一个函数,下面是它的源码:
这个函数有三个参数,其中两个都有默认值,也就是默认不需要传递,context是指coroutine的上下文,默认是DefaultDispatcher,DefaultDispatcher当前的实现是CommonPool,由于目前还是experimental,后面说不定会更改成其他的实现。
CommonPool默认会使用ForkJoinPool作为coroutine的调度策略,如果不存在则fallback到线程池的策略,ForkJoinPool实现了work-stealing算法,当前线程的工作完成后从其他线程的待执行任务中窃取,具体的解释推荐直接看其注释,比网上的博客清晰的多。
而第二个参数
CoroutineStart
指的是coroutine启动的选项,总的来说有四种:第三个就是实际的coroutine要执行的代码段了,下面我们看看具体的执行流程:
下面就会调用CoroutineStart中对应的invoke方法:
接着会去调用
startCoroutineCancellable
方法:上面说到编译器会生成内部类,那么我们看看这里到底有什么黑魔法,下面贴一下具体的结构
![image](https://user-images.githubusercontent.com/7877752/31268477-14b2237c-aa42-11e7-9c41-6d6ffe5d4fd3.png)
反编译之后,先只看create方法:
创建完需要的上下文之后,会去调用
拿到Continuation
后,就去调用resumeCancellable
方法:可以看到最终就是丢到CommonPool中(ForkJoinPool),不过在那之前会包装成一个DispatchTask:
在我们的场景下最终会调用:
Continuation#public fun resume(value: T)
,这里的实际会调用:这里doResume就是上文提到Kotlin编译器生成的内部类:
可以看到实际上Kotlin使用状态机实现的Coroutine,根据label的状态决定要执行的代码块,Kotlin会在编译时根据可suspend的方法插入相应的label,从而实现主动让出线程资源,并且将本地变量保存到Continuation的实例变量中,等到下次得到调度的时候,根据label来决定要执行的代码块,等到函数实际执行完的时候则直接返回对应的返回值(没有的话则是默认值).
比如看下面这段代码:
这段代码总共有三种状态:
每一个状态都是continuation的入口点之一,这段代码会编译为一个实现了状态机的匿名类,有一个状态变量用于保存当前状态机的状态,以及用于保存当前coroutine的本地变量,编译后的代码用Java代码类似下面:
当coroutine开始执行的时候,默认label为0,那么就会跳转到L0,然后执行一个耗时的业务逻辑,将label设置为1,调用await,如果coroutine的执行需要暂停的那么就返回掉。当需要继续执行的时候就再次调用resume(),这次就会跳转到L1, 执行完业务逻辑后,将label设置为2,调用await并根据是否需要暂停来return,下次的继续调度,这次会从L2开始执行,然后label设置为-1,意味着不需要执行完了,不需要再调度了。
回到最初的代码段,我们首先调用了delay方法,这个方法默认使用ScheduledExecutorService,从而将当前的coroutine上下文包装到DispatchTask,再对应的延迟时间之后再恢复执行,恢复执行之后,这时候label是1,那么就会直接进入第二段代码,输出
World!
总结
本文大致讲解了一些Kotlin中Coroutine的实现原理,当然对于协程,很多编程语言都有相关的实现,推荐都看一下文档,实际使用对比看看。
![image](https://user-images.githubusercontent.com/7877752/31280855-60a4240c-aa73-11e7-9df2-34ee9db104f2.png)
参考资料
The text was updated successfully, but these errors were encountered: