基本概念
协程的诞生和使用对我们android开发者来说大家都不陌生,且项目中基本上都会用到。刚开始我们接触的时候对协程的就有一些基本概念。第一个概念:它是一个线程框架可以更方便的实现异步调用,很容易实现线程切换。
第二个概念:它另外一个功能是非阻塞式挂起函数,它帮助开发者消除了回调,可以使用同步的代码写出异步的操作,当然也就消除了一些业务场景的回调地狱。
接下来本文通过源码角度分析,了解协程的本质以及协程的创建,启动,启动策略,线程切换(Dispathchers),最终协程任务执行。
-
如何创建一个协程
-
协程是如何被创建出来的
-
1)协程启动策略是什么? 2)启动策略完成了什么工作
-
协程是如何被启动的
-
协程启动过程中的Dispatchers是什么,Dispathchers做了什么工作(调度器)
-
Worker是什么,Worker中的任务被找到后是如何执行的
-
newCoroutineContext(context)
-
总结
1.如何创建一个协程
创建协程的方式有3种:launch, async, runBlocking,
//launch方式
private fun launch() {
CoroutineScope(Job()).launch(Dispatchers.Default) {
delay(1000L)
Log.d(TAG, "Kotlin")
}
Log.d(TAG, "Hello")
Thread.sleep(2000L)
}
//async方式
private fun async() {
runBlocking {
val deferred = async {
Log.d(TAG, "Hello")
delay(1000L)
Log.d(TAG, "Kotlin")
}
deferred.await()
}
}
//runBlocking方式
private fun runBlocking() {
runBlocking(Dispatchers.Default) {
Log.d(TAG, "launch started!")
delay(1000L)
Log.d(TAG, "Kotlin")
}
Log.d(TAG, "Hello")
Thread.sleep(2000L)
Log.d(TAG, "Process end!")
}
三者区别如下:
- launch:无法获取执行结果,返回类型Job,不会阻塞;
- async:可获取执行结果,返回类型Deferred,调用await()会阻塞,不调用则不会阻塞但也无法获取执行结果,它像是结合了 launch 和 runBlocking 两者的优点;
- runBlocking:可获取执行结果,阻塞当前线程的执行,多用于Demo、测试,官方推荐只用于连接线程与协程。
以launch为例,看一下它的源码
public fun CoroutineScope.launch(
//1.context
context: CoroutineContext = EmptyCoroutineContext,
//2.start
start: CoroutineStart = CoroutineStart.DEFAULT,
//3.block
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
launch源码有三个参数分别对其解释一下:
- context: 协程的上下文,用于提供协程启动和运行时需要的信息,默认值是EmptyCoroutineContext,有默认值就可以不传,但是也可以传递Kotlin提供的Dispatchers来指定协程运行在哪一个线程中
- start: 协程的启动模式;(目前有4种启动策略 第3点会介绍)
- block: 这个可以理解为协程的函数体,函数类型为
suspend CoroutineScope.() -> Unit
通过对参数的说明,关于launch的创建也可以分开写:
private fun launch2() {
val coroutineScope = CoroutineScope(Job())
val block: suspend CoroutineScope.() -> Unit = {
Log.d(TAG, "Hello")
delay(1000L)
Log.d(TAG, "Kotlin")
}
coroutineScope.launch(block = block)
}
反编译成java代码后是这样的(onCrate()在第三点讲解启动策略会用到)
private final void launch2() {
CoroutineScope coroutineScope = CoroutineScopeKt.CoroutineScope((CoroutineContext)JobKt.Job$default((Job)null, 1, (Object)null));
//Function2 是Kotlin 为 block 变量生成的静态变量以及方法。
//实现状态机的逻辑
Function2 block = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
ResultKt.throwOnFailure($result);
Log.d(ScopeActivityV2.this.TAG, "Hello");
this.label = 1;
if (DelayKt.delay(1000L, this) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
Log.d(ScopeActivityV2.this.TAG, "Kotlin");
return Unit.INSTANCE;
}
//2.第三点讲解启动策略会看到
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
});
BuildersKt.launch$default(coroutineScope, (CoroutineContext)null, (CoroutineStart)null, block, 3, (Object)null);
}
2.协程是如何被创建出来的
public fun CoroutineScope.launch(
//1.context
context: CoroutineContext = EmptyCoroutineContext,
//2.start
start: CoroutineStart = CoroutineStart.DEFAULT,
//3.block
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
//4.这里只有一个start的方法....创建协程的地方
coroutine.start(start, coroutine, block)
return coroutine
}
coroutine.start为协程的创建与启动,这个start进入到了AbstractCoroutine,是一个抽象类,里面的start方法专门用于启动协程。
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
...
...
...
/**
* 用给定的block和start启动协程
* 最多调用一次
* 该方法的写法等同于start.invoke(block, receiver, this)
* 因此调用的CoroutineStart类的方法
*/
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
start(block, receiver, this)
}
}
AbstractCoroutine中的start函数负责启动协程,同时启动是根据block和启动策略决定的,那么启动策略是什么? 以及启动策略完成了什么工作?
3.协程启动策略是什么? 启动策略完成了什么工作?
1.)协程的启动策略
public enum class CoroutineStart {
/**
* 根据上下文立即调度协程执行。
* [immediately schedules coroutine for execution according to its context]
*/
DEFAULT,
/**
* 延迟启动协程,只在需要时才启动。
* 如果协程[Job]在它有机会开始执行之前被取消,那么它根本不会开始执行
* 而是以一个异常结束。
* [starts coroutine lazily, only when it is needed]
*/
LAZY,
/**
*以一种不可取消的方式,根据其上下文安排执行的协程;
*类似于[DEFAULT],但是协程在开始执行之前不能取消。
*协程在挂起点的可取消性取决于挂起函数的具体实现细节,如[DEFAULT]。
*[atomically (in a non-cancellable way) schedules coroutine for execution according to its context]
*/
ATOMIC,
/**
* 立即执行协程,直到它在当前线程中的第一个挂起点;
* [immediately executes coroutine until its first suspension point _in the current thread_.]
*/
UNDISPATCHED;
}
2.)协程在确定启动策略之后就会开始执行它的任务,它的任务在invoke()函数中被分为不同的执行方式.从而触发CoroutineStart#invoke() 启动策略完成的工作
public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =
when (this) {
DEFAULT -> block.startCoroutineCancellable(completion)
ATOMIC -> block.startCoroutine(completion)
UNDISPATCHED -> block.startCoroutineUndispatched(completion)
LAZY -> Unit // will start lazily
}
/**
* 用这个协程的启动策略启动相应的block作为协程
* [Starts the corresponding block as a coroutine with this coroutine's start strategy.]
/
从上面注释能看出来,协程启动策略确定好之后,就开始下一步创建/启动协程
这里直接对block.startCoroutine(completion)进行分析,block.startCoroutineCancellable和block.startCoroutineUndispatched也只是在startCoroutine的基础上增加了一些额外的功能,前者表示启动协程以后可以响应取消,后者表示协程启动以后不会被分发。
public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
/**
* 启动一个没有接收器且结果类型为[T]的协程。
* 每次调用这个函数时,它都会创建并启动一个新的、可挂起计算实例。
* 当协程以一个结果或一个异常完成时,将调用[completion]延续。
*/
//接着上面,看看createCoroutineUnintercepted()做了哪些工作
//1.expect 声明
public expect fun <T> (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation<T>
): Continuation<Unit>
expect的意思是期望、期盼,这里可以理解为一种声明,期望在具体的平台中实现。
进入到createCoroutineUnintercepted()的源码中看到并没有什么实现,这主要是因为Kotlin是面向多个平台的具体的实现需要在特定平台中才能找到,我们以JVM做为分析。这里进入IntrinsicsJvm.kt中分析。
//IntrinsicsJvm#createCoroutineUnintercepted
//actual代表的是createCoroutineUnintercepted在JVM平台上的具体实现
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
//1.重点,创建协程地方
create(receiver, probeCompletion)
else {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
}
}
这里的actual就是在具体平台上的实现。
但上面有一个难点,这个[this] 是什么含义呢.如果只是在反编译代码或者源码中去看很难发现它是什么.所以需要结合字节码来看。还记得在第一点说“如何创建一个协程”有反编译代码后但并没发现什么。所以这次去查看字节码
final class com/scope/ScopeActivityV2block$1 是block具体的实现类。它继承自kotlin/coroutines/jvm/internal/SuspendLambda
//代码1.
internal abstract class SuspendLambda(
public override val arity: Int,
completion: Continuation<Any?>?
) : ContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction {
constructor(arity: Int) : this(arity, null)
public override fun toString(): String =
if (completion == null)
Reflection.renderLambdaToString(this) // this is lambda
else
super.toString() // this is continuation
}
//代码2.
internal abstract class ContinuationImpl(
completion: Continuation<Any?>?,
private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
...
...
...
}
//代码3.
internal abstract class BaseContinuationImpl(
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
// 创建一个Continuation对象返回
public open fun create(completion: Continuation<*>): Continuation<Unit> {
throw UnsupportedOperationException("create(Continuation) has not been overridden")
}
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
}
...
...
}
继承链:SuspendLambda->ContinuationImpl->BaseContinuationImpl->Continuation
SuspendLambda是ContinuationImpl的子类,ContinuationImpl又是BaseContinuationImpl的子类, 所以可以得到结论if (this is BaseContinuationImpl)的结果为true, 然后会进入到create(probeCompletion)函数中
这个create()函数抛出了一个异常,意思就是方法没有被重写,潜台词就是create()这个方法是要被重写的,如果不重写就会抛出异常。 那么create()方法又是在哪里重写的呢。答案就在反编译后的Java代码中的create方法
//这段代码来自launch创建的反编译后的Java代码
//create函数被重写
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
这行代码,其实就对应着协程被创建的时刻。到这里创建终于就结束了 。既然我们已经知道协程怎么创建了,那接下来继续来研究“协程是如何启动的呢“。
分析完了startContinue()再来分析一下startCoroutineCancellable()做了什么,因为协程默认的启动策略是CoroutineStart.DEFAULT
//1.startCoroutineCancellable
@InternalCoroutinesApi
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))
}
//2.startCoroutine
public fun <T> (suspend () -> T).startCoroutine(completion: Continuation<T>) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
通过对比可以发现startCoroutineCancellable()和startCoroutine()的内部并没有太大区别,他们最终都会调用createCoroutineUnintercepted(),只不过前者在最后调用了resumeCancellableWith(),后者调用的是resume().
4.协程是如何被启动的
协程的创建分析完成后再来分析一下协程是如何启动的,再回过头看一下createCoroutineUnintercepted()之后做了什么
public fun <T> (suspend () -> T).startCoroutine(
completion: Continuation<T>
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
进入到 intercepted,它也是需要找到对应平台上的具体实现,这里还是以JVM平台进行分析
//expect 1.需要找具体的平台。
public expect fun <T> Continuation<T>.intercepted(): Continuation<T>
//JVM平台的实现
//IntrinsicsJvm.kt#intercepted
/**
* 使用[ContinuationInterceptor]拦截continuation。
*/
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
(this as? ContinuationImpl)?.intercepted() ?: this
在分析协程的创建过程中已经分析过上面的[this] 代表的就是block变量,所以这里的强转是成立的,那么这里的intercepted()调用的就是ContinuationImpl对象中的函数
/**
* 命名挂起函数的状态机扩展自这个类
*/
internal abstract class ContinuationImpl(
completion: Continuation<Any?>?,
private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
public override val context: CoroutineContext
get() = _context!!
@Transient
private var intercepted: Continuation<Any?>? = null
//1.下面文字解释
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
}
首先intercepted()方法会判断它的成员变量intercepted是否为空,如果为空则调用context[ContinuationInterceptor],这里从上下文中拿到dispathchers对象...
先说下ContinuationInterceptor实现CoroutineContext.Element接口,Element是集合的元素类型,所以拦截器可以作为CoroutineContext集合的一个元素存放其中。
在ContinuationInterceptor中定义了一个伴生对象Key,它的类型是CoroutineContext.Key,所以Key也是拦截器作为Element元素的索引,Key是一个伴生对象,可以通过类名访问它,则CoroutineContext[ContinuationInterceptor]就可以在集合中获取到拦截器。这里使用伴生对象作为集合元素的索引,一是伴生对象成员全局唯一,另一个通过类名访问集合元素,更直观
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
同时源码中CoroutineDispatcher是一个抽象类,并实现了拦截器的接口,也就是说调度器本质上就是一个拦截器,所有的调度器都是继承这个类来实现自身的调度逻辑。
那么问题来了上下文当中的Dispatchers对象,这个Dispatchers对象又是什么呢?接下来说说dispathers(调度器)
5.协程启动过程中的Dispatchers是什么,Dispathchers做了什么工作(调度器)
以launch的源码为主进行分析
//launch方式
private fun launch() {
//1.指定为main.如果不传是默认的
CoroutineScope(Job()).launch(Dispatchers.Main) {
delay(1000L)
Log.d(TAG, "Kotlin")
}
Log.d(TAG, "Hello")
Thread.sleep(2000L)
}
//2.传入的Dispatchers.Default表示的就是这个context
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
传入的Dispatchers.Default对应的是context参数,从源码可知这个参数不是必传的,因为它有默认值EmptyCoroutineContext,Kotlin官方用它来替代了null,这是Kotlin空安全思维。
传入Dispatchers.Default之后就是用它替代了EmptyCoroutineContext,那么这里的Dispatchers的定义跟CoroutineContext有什么关系呢?看一下Dispatchers的源码
public actual object Dispatchers {
// 默认调度器 @JvmStatic
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
// UI调度器 @JvmStatic
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
// 无限制调度器 @JvmStatic
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
// IO调度器 @JvmStatic
public val IO: CoroutineDispatcher = DefaultScheduler.IO
}
Dispatchers中提供了4种类型调度器:
- Default 默认调度器,适合CPU密集型任务调度器 比如逻辑计算;
- Main 在Android平台,UI调度器;
- Unconfined 无限制调度器,对协程执行的线程不做限制,协程恢复时可以在任意线程;
- IO IO调度器,适合IO密集型任务调度器 比如读写文件,网络请求等。
Dispatchers是一个单例对象,它里面的几个类型都是CoroutineDispatcher。
//1. 所有协程调度器实现扩展的基类。
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
}
//2.标记拦截协程延续的协程上下文元素
public interface ContinuationInterceptor : CoroutineContext.Element {}
//3.CoroutineContext的一个元素。协程上下文的一个元素本身就是一个单例上下文。
public interface Element : CoroutineContext {}
CoroutineDispatcher本身又是CoroutineContext,从上面的源码就可以得出他们的关系可以这么表示:
协程的运行是离不开线程的,Dispatchers的作用就是确定协程运行在哪个线程,默认是Default,然后它也可以运行在IO、Main等线程,它负责将任务调度的指定的现呈上。
通过前分析在协程中默认的是Default线程池,因此这里进入的就是Default线程池。 那么我们回到intercepted()函数继续进行分析,通过Debug进入到CoroutineDispatcher中的interceptContinuation()函数
//1.回顾代码
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))
}
//2.回顾代码 ContinuationImpl.kt
@Transient
private var intercepted: Continuation<Any?>? = null
//1.下面文字解释
public fun intercepted(): Continuation<Any?> =
intercepted
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
.also { intercepted = it }
//3.
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
/**
* 返回一个封装了提供的[continuation]的continuation,从而拦截所有的恢复。
* 这个方法通常应该是异常安全的。
* 从此方法抛出的异常可能会使使用此调度程序的协程处于不一致且难以调试的状态。
*/
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
}
interceptContinuation()返回了一个DispatchedContinuation对象,其中的[this] 就是默认的线程池Dispatchers.Default。
然后通过DispatchedContinuation调用它的resumeCancellableWith()函数,这个函数前面分析过是从哪里进入的,这里不再说明。
下面分析DispatchedContinuation的代码
internal class DispatchedContinuation<in T>(
@JvmField val dispatcher: CoroutineDispatcher,
@JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation {
...
public fun <T> Continuation<T>.resumeCancellableWith(
result: Result<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
else -> resumeWith(result)
}
//我们内联它来保存堆栈上的一个条目,在它显示的情况下(无限制调度程序)
//它只在Continuation<T>.resumeCancellableWith中使用
inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
//重点dispath代码
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
resumeUndispatchedWith(result)
}
}
}
}
...
}
DispatchedContinuation继承了DispatchedTask:
internal abstract class DispatchedTask<in T>(
@JvmField public var resumeMode: Int
) : SchedulerTask() { }
internal actual typealias SchedulerTask = Task
internal abstract class Task(
@JvmField var submissionTime: Long,
@JvmField var taskContext: TaskContext
) : Runnable {
constructor() : this(0, NonBlockingContext)
inline val mode: Int get() = taskContext.taskMode // TASK_XXX
}
DispatchedTask继承了SchedulerTask,同时SchedulerTask还是Task的别名,Task又实现了Runnable接口,这意味着它可以被分发到Java的线程中去执行了。
同时可以得出一个结论:DispatchedContinuation是一个Runnable。
DispatchedContinuation还实现了Continuation接口,它还使用了类委托的语法将接口的具体实现交给了它的成员属性continuation,那么这里对上面的结论进行补充:DispatchedContinuation不仅是一个Runnable,还是一个Continuation。
DispatchedContinuation分析完了进入它的resumeCancellableWith()函数分析:
inline fun resumeCancellableWith(
result: Result<T>,
noinline onCancellation: ((cause: Throwable) -> Unit)?
) {
val state = result.toState(onCancellation)
//①
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_CANCELLABLE
//②
dispatcher.dispatch(context, this)
} else {
//这里就是Dispatchers.Unconfined情况,这个时候协程不会被分发到别的线程,只运行在当前线程中。
executeUnconfined(state, MODE_CANCELLABLE) {
if (!resumeCancelled(state)) {
resumeUndispatchedWith(result)
}
}
}
}
- 注释①: dispatcher来自CoroutineDispatcher,isDispatchNeeded就是它的成员函数
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
/**
* 如果协程的执行应该使用[dispatch]方法执行,则返回' true '。
* 大多数dispatchers的默认行为是返回' true '。
*/
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
}
isDispatchNeeded()默认返回true,且在大多数情况下都是true, 但是也有个例外就是在它的子类中Dispatchers.Unconfined会将其重写成 false。
因为默认是true,所以接下来会进入注释②
- 注释②: 注释②调用了CoroutineDispatcher中的dispatch()方法将block代码块调度到另一个线程上,这里的线程池默认值是Dispatchers.Default所以任务被分发到Default线程池,第二个参数是Runnable,这里传入的是this,因为DispatchedContinuation间接的实现了Runnable接口。
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
/**
* 在给定的[上下文]中,将一个可运行的block调度到另一个线程上。
* 这个方法应该保证给定的[block]最终会被调用,否则系统可能会达到死锁状态并且永远不会终止。
*/
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
}
因为默认线程池是Dispatchers.Default,所以这里的dispatch()其实调用的是Dispatchers.Default.dispatch,这里的Dispatchers.Default的本质是一个单例对象DefaultScheduler, 它继承了SchedulerCoroutineDispatcher:
//继承了SchedulerCoroutineDispatcher
internal object DefaultScheduler : SchedulerCoroutineDispatcher(
CORE_POOL_SIZE, MAX_POOL_SIZE,
IDLE_WORKER_KEEP_ALIVE_NS, DEFAULT_SCHEDULER_NAME
) {
// 关闭调度程序,仅用于Dispatchers.shutdown()
internal fun shutdown() {
super.close()
}
//重写 (Dispatchers.Default as ExecutorCoroutineDispatcher).close()
override fun close() {
throw UnsupportedOperationException("Dispatchers.Default cannot be closed")
}
override fun toString(): String = "Dispatchers.Default"
}
internal open class SchedulerCoroutineDispatcher(
private val corePoolSize: Int = CORE_POOL_SIZE,
private val maxPoolSize: Int = MAX_POOL_SIZE,
private val idleWorkerKeepAliveNs: Long = IDLE_WORKER_KEEP_ALIVE_NS,
private val schedulerName: String = "CoroutineScheduler",
) : ExecutorCoroutineDispatcher() {
private var coroutineScheduler = createScheduler()
//看这里,重点调用
override fun dispatch(context: CoroutineContext, block: Runnable): Unit = coroutineScheduler.dispatch(block)
}
SchedulerCoroutineDispatcher中实际调用dispatch()方法的实际是coroutineScheduler,所以dispatcher.dispatch()实际调用的是coroutineScheduler.dispatch()
internal class CoroutineScheduler(
@JvmField val corePoolSize: Int,
@JvmField val maxPoolSize: Int,
@JvmField val idleWorkerKeepAliveNs: Long = IDLE_WORKER_KEEP_ALIVE_NS,
@JvmField val schedulerName: String = DEFAULT_SCHEDULER_NAME
) : Executor, Closeable {
//Executor接口中的方法被覆盖
override fun execute(command: Runnable) = dispatch(command)
fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, tailDispatch: Boolean = false) {
trackTask()
//将传入的 Runnable 类型的 block(也就是 DispatchedContinuation),包装成 Task。
val task = createTask(block, taskContext)
// 拿到当前的任务队列, 尝试将任务提交到本地队列并根据结果进行操作
//Worker其实是一个内部类,其实就是Java的Thread类
val currentWorker = currentWorker()
//将当前的 Task 添加到 Worker 线程的本地队列,等待执行。
val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch)
if (notAdded != null) {
if (!addToGlobalQueue(notAdded)) {
// 全局队列在关闭/关闭的最后一步关闭——不再接受任何任务
throw RejectedExecutionException("$schedulerName was terminated")
}
}
val skipUnpark = tailDispatch && currentWorker != null
//
if (task.mode == TASK_NON_BLOCKING) {
if (skipUnpark) return
signalCpuWork()
} else {
//增加阻塞任务
signalBlockingWork(skipUnpark = skipUnpark)
}
}
}
6.Worker是什么?,Worker中的任务被找到后是如何执行的
internal inner class Worker private constructor() : Thread() {
init {
isDaemon = true //守护线程默认为true
}
private fun runWorker() {
var rescanned = false
while (!isTerminated && state != WorkerState.TERMINATED) {
//在while循环中一直尝试从队列中找到任务
val task = findTask(mayHaveLocalTasks)
// 找到任务则进行下一步
if (task != null) {
rescanned = false
minDelayUntilStealableTaskNs = 0L
//1.执行任务
executeTask(task)
continue
} else {
mayHaveLocalTasks = false
}
if (minDelayUntilStealableTaskNs != 0L) {
if (!rescanned) {
rescanned = true
} else {
rescanned = false
tryReleaseCpu(WorkerState.PARKING)
interrupted()
LockSupport.parkNanos(minDelayUntilStealableTaskNs)
minDelayUntilStealableTaskNs = 0L
}
continue
}
//没有任务则停止执行,线程可能会关闭
tryPark()
}
tryReleaseCpu(WorkerState.TERMINATED)
}
}
//2.找到任务后执行
internal inner class Worker private constructor() : Thread() {
private fun executeTask(task: Task) {
val taskMode = task.mode
//当它找到一个任务时,这个工作者就会调用它
idleReset(taskMode)
beforeTask(taskMode)
runSafely(task)
afterTask(taskMode)
}
fun runSafely(task: Task) {
try {
task.run()
} catch (e: Throwable) {
val thread = Thread.currentThread()
thread.uncaughtExceptionHandler.uncaughtException(thread, e)
} finally {
unTrackTask()
}
}
}
internal abstract class Task(
@JvmField var submissionTime: Long,
@JvmField var taskContext: TaskContext
) : Runnable {
constructor() : this(0, NonBlockingContext)
inline val mode: Int get() = taskContext.taskMode // TASK_XXX
}
最终进入到runSafely()函数中,然后调用run方法,前面分析过,将DispatchedContinuation包装成一个实现了Runnable接口的Task,所以这里的task.run()本质上就是调用的Runnable.run(),到这里任务就协程任务就真正的执行了。
到了这里那么也就可以知道这里的run() 函数其实调用的就是DispatchedContinuation父类DispatchedTask中的run()函数:
internal abstract class DispatchedTask<in T>(
@JvmField public var resumeMode: Int
) : SchedulerTask() {
public final override fun run() {
assert { resumeMode != MODE_UNINITIALIZED }
val taskContext = this.taskContext
var fatalException: Throwable? = null
try {
val delegate = delegate as DispatchedContinuation<T>
val continuation = delegate.continuation
withContinuationContext(continuation, delegate.countOrElement) {
val context = continuation.context
val state = takeState() //
val exception = getExceptionalResult(state)
// 检查延续最初是否在异常情况下恢复。
// 如果是这样,它将主导取消,否则原始异常将被静默地丢失。
val job = if (exception == null && resumeMode.isCancellableMode) context[Job] else null
if (job != null && !job.isActive) {
//①
val cause = job.getCancellationException()
cancelCompletedResult(state, cause)
continuation.resumeWithStackTrace(cause)
} else {
if (exception != null) {
//②
continuation.resumeWithException(exception)
} else {
//③
continuation.resume(getSuccessfulResult(state))
}
}
}
} catch (e: Throwable) {
//
fatalException = e
} finally {
val result = runCatching { taskContext.afterTask() }
handleFatalException(fatalException, result.exceptionOrNull())
}
}
}
注释①: 在代码执行之前这里会判断当前协程是否被取消。如果被取消了就会调用 continuation.resumeWithStackTrace(cause)将具体的原因传出去;
注释②: 判断协程是否发生了异常如果已经发生异常就调用continuation.resumeWithException(exception)将异常传递出去;
注释③: 如果前面运行没有问题,就进入最后一步continuation.resume(getSuccessfulResult(state),此时协程正式被启动并且执行launch当中传入的block或者Lambda函数。
到这里协程就算是启动完成。同时这里也是协程与线程产生关联的地方。
7.newCoroutineContext(context)
launch源码中第一行代码做了什么目前还不得而知,这里分析一下做了什么事
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
// 就是这一行
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
//2.
/**
* 为新的协程创建上下文。当没有指定其他调度程序或[ContinuationInterceptor]时,
* 它会设置[Dispatchers.Default],并添加对调试工具的可选支持(当打开时)。
*/
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
//①
val combined = coroutineContext.foldCopiesForChildCoroutine() + context
//②
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
//③
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
- 注释①:这行代码首先调用了coroutineContext,这是因为newCoroutineContext()是CoroutineScope的扩展函数,CoroutineScope对CoroutineContext进行了封装,所以newCoroutineContext()函数中可直接访问CoroutineScope的coroutineContext;foldCopiesForChildCoroutine()函数返回子协程要继承的[CoroutineContext];然后跟传入的context参数进行合并。这行代码就是让子协程可以继承父协程的上下文元素。
- 注释②:它的作用是在调试模式下,为我们的协程对象增加唯一的 ID,这个ID就是在调试协程程序时出现的日志如:Thread:DefaultDispatcher-worker-1 @coroutine#1中的@coroutine#1,其中的1就是这个ID。
- 注释③:如果合并后的combined没有指定调度程序就默认使用Dispatcher.Default。
通过上面的分析可以得出newCoroutineContext函数确定了默认使用的线程池是Dispatcher.Default,那么这里为什么会默认使用Default线程池而不是Main呢? 因为Kotlin并不是只针对Android开发的,它支持多个平台Main线程池仅在UI相关的平台中才会用到,而协程是不能脱离线程运行的,所以这里默认使用Default线程池。
8.总结
1. 协程是如何被创建的?
协程在确认自己的启动策略后进入到createCoroutineUnintercepted函数中创建了协程的Continuation实例,Continuation的实现类是ContinuationImpl,它继承自BaseContinuationImpl,在BaseContinuationImpl中调用了它的create() 方法,而这个create() 方法需要重写才可以实现否则会抛出异常,那么这个 create() 的重写就是反编译后的Java代码中create() 函数。
2. 协程是如何被启动的?
协程通过createCoroutineUnintercepted函数创建后紧接着就会调用它的 intercepted() 方法,将其封装成 DispatchedContinuation 对象,DispatchedContinuation 是Runnable 的子类,DispatchedContinuation会持有CoroutineDispatcher以及前面创建的Continuation对象,DispatchedContinuation调用内部的resumeCancellableWith()方法,然后进入到resumeCancellableWith() 中的dispatched.dispatch() ,这里会将协程的Continuation包装成Task并添加到Worker的本地任务队列等待执行。而这里的Worker本质上是Java中的Thread,在这一步协程完成了线程的切换,任务添加到Worker的本地任务队列后就会通过run()方法启动任务,这里调用的是task.run() ,这里的run最终是调用的DispatchedContinuation的父类DispatchedTask中的run()方法,在这个run方法中如果前面没有异常最终会调用continuation.resume() ,然后就开始执行执行协程体中的代码了也就是反编译代码中的invokeSuspend() ,这里开始了协程状态机流程,这样协程就被启动了。
3.启动流程链路
-
CoroutineScope#launch()创建一个协程,在其内部实现中根据启动模式为CoroutineStart.DEFAULT,创建一个StandaloneCoroutine协程对象,并触发StandaloneCoroutine#start(start, coroutine, block);
-
StandaloneCoroutine的父类是AbstractCoroutine,StandaloneCoroutine#start()的实现在其父类中,即AbstractCoroutine#start();
-
在AbstractCoroutine#start()中,触发CoroutineStart#invoke();
-
CoroutineStart#invoke()的处理逻辑中,根据调度器为Dispatchers.Default,调用协程体的startCoroutineCancellable()方法;
-
startCoroutineCancellable()的内部处理是一个链式调用:
- createCoroutineUnintercepted(..).intercepted().resumeCancellableWith(Result.success(Unit))
- createCoroutineUnintercepted()创建一个协程体类对象;
- intercepted()使用拦截器(调度器)将协程体类对象包装成DispatchedContinuation(DispatchedContinuation代理了协程体类Continuation对象,并持有调度器);
- 调用DispatchedContinuation#resumeCancellableWith()。
-
在DispatchedContinuation#resumeCancellableWith()中,使用线程调度器触发dispatcher#dispatch(context, this)进行调度,该调度器为Dispatchers.Default;
-
Dispatchers.Default#dispatch()调度处理中,将DispatchedContinuation分发到CoroutineScheduler线程池中,由CoroutineScheduler分配一个线程Worker,最终在Woreder的run()方法中触发了DispatchedContinuation的run(),其内部实现是使协程体Continuation对象的resumeWithI()得以执行,前文中分析到协程体的执行其实就是resumeWith()方法被调用,这样协程体就可以在执行的线程中执行了