协程是一种轻量级的线程?

作为一名成熟的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  interceptContinuation(continuation: Continuation): Continuation =
        DispatchedContinuation(this, continuation)

一旦 Continuation 对象需要在另外的 Dispatcher 中执行,DispatchedContinuation 的 resumeWith 方法会负责将协程分发到合适的 Dispatcher。我们要知道的一点是DispatchedContinuation 实现了Runnable 接口,可以被线程池执行。

override fun resumeWith(result: Result) {
        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)
微信图片_20210610162457.jpg

今天的内容就介绍到这里了,期待下次相见

你可能感兴趣的:(协程是一种轻量级的线程?)