【问题标题】:How are Kotlin coroutine elements accessible in the current context?在当前上下文中如何访问 Kotlin 协程元素?
【发布时间】:2020-03-16 05:18:19
【问题描述】:

我正在尝试了解协程在 Kotlin 中的工作原理。我已经在线阅读了几篇文章,包括这篇文章 - https://proandroiddev.com/demystifying-coroutinecontext-1ce5b68407ad - 但我仍然对如何访问当前上下文中的协程元素感到有些困惑。这是让我感到困惑的部分。

当前上下文中的元素可以通过顶层挂起的coroutineContext只读属性获取。

println("Running in ${coroutineContext[CoroutineName]}")

在这种情况下,CoroutineName 是对映射到 CoroutineName 元素的键的引用。我知道coroutineContext 上的get 方法会查看提供给它的键的通用类型(在本例中为CoroutineName)以获取适当的元素。我想了解的是CoroutineName,或者就此而言,JobCoroutineExceptionHandlerCoroutineDispatcher 甚至可以在当前范围内引用,当它们不是CoroutineScope 接收器的属性时.

【问题讨论】:

  • 我不确定你到底在问什么,但这里有一个猜测:当你取消引用全局 coroutineContext 属性时,你在某种程度上触及了一个“神奇”的 Kotlin 内在函数无法通过检查源代码来解释,为您提供当前有效的协程上下文。源码here.
  • 当你说,launch 一个新的协程时,coroutineContext 可以作为CoroutineScope 的成员使用,它是你传递给launch 的块上的接收器.但是,我想知道的是我们如何能够访问coroutineContext[CoroutineName]CoroutineName 在当前范围内如何可供我们用作coroutineContext 提供的地图的索引?

标签: kotlin kotlin-coroutines


【解决方案1】:

所有coroutine context elements 都有一个属性key 用于在协程上下文中寻址元素(这是一种可寻址集合)。 CoroutineName 和其他上下文元素一样,将键定义为对象:

companion object Key : Key<CoroutineName>

In Kotlin,你可以使用类名访问类的伴生对象:

伴生对象是一个单例对象,可以通过包含类的名称直接访问其成员(尽管如果您想明确访问伴生对象,也可以插入伴生对象的名称)

所以,我认为这两个表达式会是一样的:

coroutineContext[CoroutineName]
coroutineContext[CoroutineName.Key]

关于 SO 上的伴生对象的另一个好问题:What is the point of naming a companion object in kotlin

顺便说一句,您可以在 IDEA 中按 ctrl + 单击 coroutineContext[CoroutineName] 中的 CoroutineName 并自己查看声明。

【讨论】:

    【解决方案2】:

    全局 coroutineContext 属性的实现是内在的,这意味着没有就其实际实现方式做出任何承诺。

    但是在 JVM 上,属性本质上是一个线程局部变量。当一个协程被挂起时,当前的协程上下文被记住在创建的Continuation 对象中。当协程恢复时,上下文从恢复的延续复制回coroutineContext 属性所引用的线程局部变量。

    【讨论】:

      【解决方案3】:

      您可以运行以下一些简单代码来阐明您的理解:

      fun main() {
          GlobalScope.launch {
              println(this)
              println(GlobalScope)
          }
          Thread.sleep(100)
      }
      

      它会打印出来

      StandaloneCoroutine{Active}@b4642b3
      kotlinx.coroutines.GlobalScope@72445c73
      

      这应该让您清楚,您启动协程的范围不是您的协程构建器块的接收者范围。您获得的范围是由 launch 函数创建的,该函数使用名称和您看到的所有其他内容填充它。

      这个机制更神奇的部分是coroutineContext 不仅在launch 内部可用,而且在任何suspend fun 中都可用,作为全局属性。它的计算结果与您在 launch 块中看到的上下文相同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-22
        • 1970-01-01
        • 1970-01-01
        • 2021-02-25
        • 2022-12-14
        • 2018-10-25
        • 2023-03-11
        相关资源
        最近更新 更多