作为一名成熟的Android开发者,"协程是一种轻量级的线程"这句话我们可能已经听了无数遍了,但有多少同学能真正的理解这句话呢? 接下来的内容会带大家详细的了解协程与线程的关系,以及协程是如何运行的。
协程和线程:
当我们启动一个协程时,协程lambda表达式中的代码会在专门的线程中执行,例如
launch(Dispatchers.Default) {
delay(2000L) // 模拟一些耗时操作
println("协程代码执行完毕!") // 在延迟后打印输出
}
上面的launch代码块,会被分发到由协程库所管理的线程池中执行,上面的例子中的线程池属于Dispatchers.Default。该代码块会在未来的某个时间,在线程中的某个线程中执行,具体的执行时间取决于线程池的策略。
有些小伙伴可能会有疑问了,你怎么知道Dispatchers.Default是一个线程池呢,是哪种类型的线程池呢,我们进入到Dispatchers的源码(如下),我们会发现Dispatchers.Default是一个共享线程池,他的线程数是由CPU的核心数量有关系,但是最少也会有两个线程,其他类型的分发器不再做详细讲解,我们只需要知道,协程是基于线程池实现的。所以,协程的性能好坏是要跟线程吃对比的,而不是线程。
/**
* The default [CoroutineDispatcher] that is used by all standard builders like
* [launch][CoroutineScope.launch], [async][CoroutineScope.async], etc
* if no dispatcher nor any other [ContinuationInterceptor] is specified in their context.
*
* It is backed by a shared pool of threads on JVM. By default, the maximal level of parallelism used
* by this dispatcher is equal to the number of CPU cores, but is at least two.
* Level of parallelism X guarantees that no more than X tasks can be executed in this dispatcher in parallel.
*/
@JvmStatic
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
工作原理
从协程被创建到协程被线程执行,中间经历了一个什么过程呢?当我们使用launch、async等方式创建协程时,可以指定CoroutineDispatcher(协程调度器),如果不指定,会默认使用上问所说的Dispatchers.Default调度器。
CoroutineDispatcher 会负责将协程的执行分配到具体的线程,在底层,当 CoroutineDispatcher 被调用时,它会调用封装了 Continuation (比如这里的协程) interceptContinuation 方法来拦截协程。该流程是以 CoroutineDispatcher 实现了 CoroutineInterceptor 接口作为前提。
/**
* Returns a continuation that wraps the provided [continuation], thus intercepting all resumptions.
*
* This method should generally be exception-safe. An exception thrown from this method
* may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
*/
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
一旦 Continuation 对象需要在另外的 Dispatcher 中执行,DispatchedContinuation 的 resumeWith 方法会负责将协程分发到合适的 Dispatcher。我们要知道的一点是DispatchedContinuation 实现了Runnable 接口,可以被线程池执行。
override fun resumeWith(result: Result<T>) {
val context = continuation.context
val state = result.toState()
if (dispatcher.isDispatchNeeded(context)) {
_state = state
resumeMode = MODE_ATOMIC_DEFAULT
dispatcher.dispatch(context, this)
} else {
executeUnconfined(state, MODE_ATOMIC_DEFAULT) {
withCoroutineContext(this.context, countOrElement) {
continuation.resumeWith(result)
}
}
}
}
Dispatcher 会调用 dispatch方法,dispatch方法就是将协程交给指定的线程池执行。所以以后别再拿协程跟线程作比较了,而要和线程池比较!
/**
* Dispatches execution of a runnable [block] onto another thread in the given [context].
* This method should guarantee that the given [block] will be eventually invoked,
* otherwise the system may reach a deadlock state and never leave it.
* Cancellation mechanism is transparent for [CoroutineDispatcher] and is managed by [block] internals.
*
* This method should generally be exception-safe. An exception thrown from this method
* may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
*
* This method must not immediately call [block]. Doing so would result in [StackOverflowError]
* when [yield] is repeatedly called from a loop. However, an implementation that returns `false` from
* [isDispatchNeeded] can delegate this function to `dispatch` method of [Dispatchers.Unconfined], which is
* integrated with [yield] to avoid this problem.
*/
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
今天的内容就介绍到这里了,期待下次相见