koltin,与直接使用CoroutineScope和从CoroutineScope派生类有什么不同

时间:2020-07-31 17:06:47

标签: kotlin-coroutines coroutinescope

启动协程时,它可能只创建一个onStop并从中调用launch {}-CoroutineScope

或从doSomething_2()派生该类,并使用要启动的类{}。 -CoroutineScope

这两者之间是否有区别,哪种方法更可取?

doSomething_1()

2 个答案:

答案 0 :(得分:3)

这两者之间是否有区别,哪种方法更可取?

是的,有一个根本的区别,那就是一个正确而另一个错误。它与结构化并发有关:如果您的AClass是“工作单元”的根对象,无论它是什么,并且负责其生命周期(或观察者),那么它也应该是根您将在其中启动协程的范围。生命周期结束时,AClass应该通过在自身上调用cancel来取消根作用域,从而将该事件传播到协程子系统。 CoroutineScope.cancel是扩展功能。

我接受了您的代码并进行了以下修复:

  1. CoroutineScope.coroutineContext里面必须有Job(),所以我添加了它。我删除了调度程序,因为它与该故事无关,并且Main调度程序用于GUI,而我们正在运行一个简单的测试。

  2. 我删除了您的dispose()函数,我们有cancel()开箱即用。

  3. 我删除了theJob1theJob2字段,因为一旦正确开始使用结构化并发,它们就毫无用处。

我还添加了一些代码,使我们能够观察到这种行为:

  1. 在每个协程中添加一个delay和一个println,以查看完成的时间。

  2. 添加了main函数以对其进行测试。该功能永远在最后一行阻塞,以便我们可以查看启动的协程将执行的操作。

这是代码:

import kotlinx.coroutines.*
import java.lang.Thread.currentThread
import kotlin.coroutines.CoroutineContext

fun main() {
    val a = AClass()
    a.doSomething_1()
    a.doSomething_2()
    a.cancel()
    currentThread().join()
}

class AClass : CoroutineScope {

    override val coroutineContext: CoroutineContext = Job()

    fun doSomething_1() {
        launch(Dispatchers.IO) {
            try {
                delay(10_000)
            } finally {
                println("theJob1 completing")
            }
        }
    }

    fun doSomething_2() {
        CoroutineScope(Dispatchers.IO).launch {
            try {
                delay(10_000)
            } finally {
                println("theJob2 completing")
            }
        }
    }
}

运行它时,您只会看到theJob1完成而theJob2运行了整整10秒钟,而没有遵循cancel信号。

这是因为构造CoroutineScope(Dispatchers.IO)会创建一个独立的作用域,而不是成为AClass范围的子级,从而破坏了协程的层次结构。

从理论上讲,您仍然可以使用显式的CoroutineScope构造函数来保持层次结构,但随后您将发现显然不是首选的方法:

CoroutineScope(coroutineContext + Dispatchers.IO).launch {

这等同于

launch(Dispatchers.IO) {

答案 1 :(得分:1)

这两个协程将在相同的上下文中启动。您可以通过在两个位置同时打印协程上下文来查看此内容:

launch(Dispatchers.IO) {
    println("doSomething_1 context: ${coroutineContext}")
}
CoroutineScope(Dispatchers.IO).launch {
    println("doSomething_2 context: ${coroutineContext}")
}

这将打印如下内容:

doSomething_1 context: [StandaloneCoroutine{Active}@7b8cce78, Dispatchers.IO]
doSomething_2 context: [StandaloneCoroutine{Active}@3c938006, Dispatchers.IO]

我还没有看到CoroutineScope在内部协程代码之外经常实现的情况。在这种情况下,您应该偏重于继承而不是继承,尤其是因为CoroutineContext可以使用+运算符构成其核心。例如,当您launch使用新的协程时,现有上下文将与您提供的新上下文简单组合。

进一步阅读: