Kotlin协程的JVM实现源码分析(上)

本文从协程的启动launch源码入手分析,协程JVM实现分为两篇:

  1. 协程启动和执行源码分析
  2. 无栈协程 和 Continuation

基本环境:

  • IntelliJ IDEA 2023.3.2
  • Kotlin 1.8.20
  • kotlinx-coroutines-core 1.7.3
  • gradle 8.2

一、协程的启动和执行

GlobalScope.launch 启动协程分析:

kotlin 复制代码
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
}

调用关系: CoroutineScope.launch -> StandaloneCoroutine.start ->
CoroutineStart.invoke -> block.startCoroutineCancellable

1. startCoroutineCancellable 启动流程

启动协程,默认 执行 block.startCoroutineCancellable 扩展方法:

kotlin 复制代码
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R, completion: Continuation<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
) =
    runSafely(completion) {
        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
    }

说明:

  1. createCoroutineUnintercepted, 创建 Continuation;
  2. intercepted,拦截生成 DispatchedContinuation;
  3. resumeCancellableWith,调度器 派发 执行协程。
intercepted()

关键在 intercepted() 方法:

  • 获取 ContinuationInterceptor,默认值是 Dispatchers.Default,
  • 使用 CoroutineDispatcher.interceptContinuation 生成 DispatchedContinuation
kotlin 复制代码
// ContinuationImpl 拦截方法
@Transient
private var intercepted: Continuation<Any?>? = null

public fun intercepted(): Continuation<Any?> =
    intercepted
        ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
            .also { intercepted = it }

// Dispatchers.Default 源码
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
    DispatchedContinuation(this, continuation)
resumeCancellableWith

DispatchedContinuation.resumeCancellableWith 实现了 调度器(线程池)的任务 派发,也就是 Runnable

kotlin 复制代码
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 {
        executeUnconfined(state, MODE_CANCELLABLE) {
            if (!resumeCancelled(state)) {
                resumeUndispatchedWith(result)
            }
        }
    }
}

默认执行到
dispatcher.dispatch(context, this),此时调度器 派发 执行。
DispatchedContinuation 实现了 Runnable 接口, run() 调用即开始执行阶段,接下来分析。

2. DispatchedContinuation

DispatchedContinuation 继承 DispatchedTask,委托了 Contanuation

协程的最终执行:

DispatchedContinuation.resumeCancellableWith -> dispatcher.dispatch()

-> DispatchedTask.run() -> DispatchedContinuation.continuation.resumeWith

最终 DispatchedContinuation.continuation 也就是我们 launch {} 块生成的 SuspendLambda 类对象。

ContinuationImpl

无论是 launch {} 块生成的 SuspendLambda类,还是 suspend fun 函数 生成 ContinuationImpl 匿名类。

它们都 继承 BaseContinuationImpl

基类 BaseContinuationImpl,实现了 resumeWith

kotlin 复制代码
try {
    val outcome = invokeSuspend(param)
    if (outcome === COROUTINE_SUSPENDED) return
    Result.success(outcome)
} catch (exception: Throwable) {
    Result.failure(exception)
}

调用了 抽象 invokeSuspend,也就是 launch {} 块编译后的代码。

执行完成后,会执行 协程 completion.resumeWith(outcome),最终完成。

resumeWith -> onCompletionInternal -> onCompleted 或 onCancelled

launch {} 块编译代码分析

launch {}async {} 编译后,都继承 SuspendLambda

反编译.class,通过 jadx-gui 看到 块 代码一般是下面 形式:

java 复制代码
public static final class AnonymousClass1 extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> {
    int label;

    @NotNull
    public final Continuation<Unit> create(@Nullable Object value, @NotNull Continuation<?> continuation) {
        return new AnonymousClass1(continuation);
    }

    @Nullable
    public final Object invoke(@NotNull CoroutineScope p1, @Nullable Continuation<? super Unit> continuation) {
        return create(p1, continuation).invokeSuspend(Unit.INSTANCE);
    }

    @Nullable
    public final Object invokeSuspend(@NotNull Object $result) {
        // launch {} 执行代码块 逻辑,会编译到这里
        ...
    }
}

小结

通过launch启动协程源码分析,了解了:

  • Dispatchers 调度器何时派发任务
  • 协程 Continuation 挂起、执行 和 恢复

补充类图

Continuation

Continuation context resumeWith(result) BaseContinuationImpl resumeWith(result) invokeSuspend(result) ContinuationImpl SuspendLambda RestrictedContinuationImpl RestrictedSuspendLambda

DispatchedContinuation

DispatchedContinuation dispatcher continuation resumeWith(result) resumeCancellableWith(result) DispatchedTask run() Runnable

关键类和文件

  1. kotlin.coroutines.Continuation.kt
  2. kotlin.coroutines.CoroutineContext
  3. kotlin.coroutines.jvm.internal.BaseContinuationImpl.kt 对应 Continuation 默认实现。
  4. kotlinx.coroutines.CoroutineStart 启动线程方式、调用

文档

相关推荐
在下雨5993 小时前
项目讲解1
开发语言·数据结构·c++·算法·单例模式
再努力"亿"点点3 小时前
Sklearn(机器学习)实战:鸢尾花数据集处理技巧
开发语言·python
饭碗的彼岸one3 小时前
C++ 并发编程:异步任务
c语言·开发语言·c++·后端·c·异步
OxYGC4 小时前
[玩转GoLang] 5分钟整合Gin / Gorm框架入门
开发语言·golang·gin
锐策4 小时前
Lua 核心知识点详解
开发语言·lua
kyle~4 小时前
C/C++---动态内存管理(new delete)
c语言·开发语言·c++
落日沉溺于海5 小时前
React From表单使用Formik和yup进行校验
开发语言·前端·javascript
鲸屿1956 小时前
python之socket网络编程
开发语言·网络·python
没有梦想的咸鱼185-1037-16636 小时前
基于R语言机器学习方法在生态经济学领域中的实践技术应用
开发语言·机器学习·数据分析·r语言
向上的车轮6 小时前
基于go语言的云原生TodoList Demo 项目,验证云原生核心特性
开发语言·云原生·golang