首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >koltin,与直接使用CoroutineScope和从CoroutineScope派生类有什么区别

koltin,与直接使用CoroutineScope和从CoroutineScope派生类有什么区别
EN

Stack Overflow用户
提问于 2020-07-31 17:06:47
回答 2查看 389关注 0票数 3

当启动协同线时,它可能只是创建一个CoroutineScope并从它调用启动{} -- doSomething_2()

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

这两者之间有什么区别,哪种方式更好呢?

代码语言:javascript
复制
class AClass : CoroutineScope {

    override val coroutineContext: CoroutineContext = Dispatchers.Main
    
    var theJob1: Job? = null
    var theJob2: Job? = null
    
    fun doSomething_1() {
        theJob1 = launch(Dispatchers.IO) {
            // ... ...
        }
    }
    
    fun doSomething_2() {
        theJob2 = CoroutineScope(Dispatchers.IO).launch {
            // ... ...
        }
    }
    
    fun dispose() {
        theJob1?.cancel()
        theJob2?.cancel()
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-08-01 07:58:24

这两者之间有什么区别,哪种方式更好呢?

是的,有一个基本的区别,使一个正确和另一个不正确。它是关于结构化并发的:如果您的AClass是您的“工作单元”(不管是什么)的根对象,并且是它生命周期的负责人(或观察者),那么它也应该是您将在其中启动的coroutine的根范围。当生命周期结束时,AClass应该通过对自身调用cancel来将该事件传播到协同子系统,从而取消根范围。CoroutineScope.cancel是一个扩展函数。

我接受了您的代码并做了以下修正:

  1. CoroutineScope.coroutineContext必须有一个Job()在里面,所以我添加了它。我删除了dispatcher,因为它与这个故事无关,而Main调度器是用于GUI的,而我们正在运行一个简单的测试。
  2. 我删除了你的dispose()函数,我们有cancel()开箱即用。
  3. 我删除了theJob1theJob2字段,因为一旦您开始正确地使用结构化并发,它们就没有用了。

我还添加了一些允许我们观察行为的代码:

  1. 在每个协同线中添加一个delay,并添加一个println来查看何时完成。
  2. 添加了一个main函数来测试它。函数将在最后一行永久阻塞,这样我们就可以看到所发射的协同线将做什么。

下面是代码:

代码语言:javascript
复制
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构造函数来保持层次结构,但是这样您就有了一些显然不是首选的方法:

代码语言:javascript
复制
CoroutineScope(coroutineContext + Dispatchers.IO).launch {

这相当于

代码语言:javascript
复制
launch(Dispatchers.IO) {
票数 3
EN

Stack Overflow用户

发布于 2020-08-01 02:54:21

这两个合作机制将在平等的背景下启动。您可以通过在这两个文件中打印coroutine上下文来看到这一点:

代码语言:javascript
复制
launch(Dispatchers.IO) {
    println("doSomething_1 context: ${coroutineContext}")
}
CoroutineScope(Dispatchers.IO).launch {
    println("doSomething_2 context: ${coroutineContext}")
}

这将打印如下内容:

代码语言:javascript
复制
doSomething_1 context: [StandaloneCoroutine{Active}@7b8cce78, Dispatchers.IO]
doSomething_2 context: [StandaloneCoroutine{Active}@3c938006, Dispatchers.IO]

我还没有看到CoroutineScope经常在内部协同代码之外被实现。在这种情况下,您应该倾向于组合而不是继承,特别是因为CoroutineContext的核心是可组合的,而+运算符是可组合的。例如,当您launch一个新的协同线时,现有的上下文与您提供的新上下文简单地结合在一起。

进一步读:

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63196192

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档