Kotlin 协程源代码泛读:协程上下文

接着上回引子讲,回顾 launch 的实现

kotlin 复制代码
// Builders.common.kt
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
}

启动协程的时候,可以指定一个 context,这个 context 可以类比 Android 中的 Context、Activity、Application,它是一个 Interface(接口、协议),顾名思义,我们可以通过它以 key/value 的形式访问协程中的各种属性,当然你也可以增加自己的属性实现一些酷炫的功能

launch 传入的 context 并不直接使用,而是通过 newCoroutineContext 进行了一次 wrap(包装),能够推测出来,框架在你传入的的 context 的基础上添油加醋

kotlin 复制代码
// nativeMain/CoutineContext.kt
internal actual val DefaultDelay: Delay = DefaultExecutor

public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
    val combined = coroutineContext + context
    return if (combined !== DefaultDelay && combined[ContinuationInterceptor] == null)
        combined + (DefaultDelay as CoroutineContext.Element) else combined
}

这里出现了几个新名词

  • DefaultExecutor
  • CoroutineContext.Element
  • CoroutineInterceptor

即使没有看过相关的源代码,也能推断出

  • DefaultExecutor:类似 Java 线程池 Executor 的东东,用于调度协程
  • CoroutineContext.Element :CoroutineContext key/value中的的value,如果你去扒源代码会发现, CoroutineContext.Element 也是一个(is-a)CoroutineContext,使用了组合设计模式
  • CoroutineInterceptor: Coroutine拦截器,可以类比一些框架中的拦截器的概念比如常用的OkHttp 里头的拦截器,这种拦截器常用来偷梁换柱!

你可能对 coroutineContext + context 感到诧异,它使用了 Kotlin 运算符重载,所以这句话的意思是说将 CoroutineScope 中的coroutineContext 和传入的 context 进行 merge(合并)

这里我们还遇到了 CoroutineContext 中一个重要的 key/value,就是 ContinuationInterceptor,return 语句的意思,大概就是如果你不显式指定一个 ContinuationInterceptor 的话,框架会给你塞个默认的,这个 Interceptor 的作用我们后面会看到(协程调度器相关)

写一段测试代码来窥探一下 CoroutineContext,有个感性的认识

kotlin 复制代码
@OptIn(InternalCoroutinesApi::class)
fun main() {
    dumpCoroutineContext()
}

fun dumpCoroutineContext() {
    runBlocking {
        println("coroutineScope: $this")
        println("coroutineContext: ${coroutineContext}")
        println("job: ${coroutineContext[Job]}")
        launch {
            println("  sub coroutineContext: ${coroutineContext}")
            println("  sub job ${coroutineContext}")
            val job = coroutineContext[Job]
            println("  parent job ${job?.parent}")
        }
    }
}

这段代码的输出大概是这样:

perl 复制代码
coroutineScope: BlockingCoroutine{Active}@1761e840
coroutineContext:[BlockingCoroutine{Active}@1761e840, BlockingEventLoop@27abe2cd]
job: BlockingCoroutine{Active}@1761e840
  sub coroutineContext: [StandaloneCoroutine{Active}@5fe5c6f, BlockingEventLoop@27abe2cd]
  sub job [StandaloneCoroutine{Active}@5fe5c6f, BlockingEventLoop@27abe2cd]
  parent job BlockingCoroutine{Completing}@1761e840

仔细揣摩程序的输出!

反复出现的对象 BlockingCoroutine@1761e840

它是(is-a)CoutineScope,又是 CorutineContext,还是 Job!

为什么 BlockingCoroutine is-a CoroutineScope?

还是需要回到 launch 扩张方法(这个方法作为协程常用方法,我们会反反复复的引用,谁叫它是入口方法呢...)

kotlin 复制代码
// Builders.common.kt
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    // 这个 block 参数,有个隐藏的 this 即 CoroutineScope
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    // 注意第二个参数 coroutine,它是 block 的Receiver(this)
    coroutine.start(start, coroutine, block)
    return coroutine
}

CoroutineContext 里面的两个 item 是怎么来的?

BlockingEventLoop 就是上面我们讲到的框架塞给我们的ContinuationInterceptor,至于为什么是 BlockingEventLoop,机智的你应该能猜到它是和 runBlocking 配套的

perl 复制代码
[StandaloneCoroutine{Active}@5fe5c6f, BlockingEventLoop@27abe2cd]

这个 Coroutine: StandaloneCoroutine{Active}@5fe5c6f 被添加进 CoroutineContext 的 细节隐藏在 Coroutine 对象的创建里头:

kotlin 复制代码
// AbstractCoroutine.kt
public abstract class AbstractCoroutine<in T>(...) :
: JobSupport(active), Job, Continuation<T>, CoroutineScope
{
   ...
   // 合并 parent context把自个儿添加到里头
   public final override val context: CoroutineContext = parentContext + this
}

这里特意将 AbstractCorouitne(Coroutine 抽样基类)继承和实现的接口罗列出来

还记得上文提过它(协程)是 CoroutineScope,又是CoroutineContext,还是 Job!

记住这个关系,对你理解协程的基本概念很重要!

细心的你会发现 Coroutine is-a Continuation?

待续!

相关推荐
hqxstudying2 小时前
Java异常处理
java·开发语言·安全·异常
我命由我123455 小时前
Kotlin 数据容器 - List(List 概述、创建 List、List 核心特性、List 元素访问、List 遍历)
java·开发语言·jvm·windows·java-ee·kotlin·list
武子康7 小时前
Java-80 深入浅出 RPC Dubbo 动态服务降级:从雪崩防护到配置中心秒级生效
java·分布式·后端·spring·微服务·rpc·dubbo
YuTaoShao9 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
源码_V_saaskw10 小时前
JAVA图文短视频交友+自营商城系统源码支持小程序+Android+IOS+H5
java·微信小程序·小程序·uni-app·音视频·交友
超浪的晨10 小时前
Java UDP 通信详解:从基础到实战,彻底掌握无连接网络编程
java·开发语言·后端·学习·个人开发
双力臂40411 小时前
Spring Boot 单元测试进阶:JUnit5 + Mock测试与切片测试实战及覆盖率报告生成
java·spring boot·后端·单元测试
Edingbrugh.南空11 小时前
Aerospike与Redis深度对比:从架构到性能的全方位解析
java·开发语言·spring
QQ_43766431412 小时前
C++11 右值引用 Lambda 表达式
java·开发语言·c++
永卿00112 小时前
设计模式-迭代器模式
java·设计模式·迭代器模式