当启动协同线时,它可能只是创建一个CoroutineScope并从它调用启动{} -- doSomething_2(),
或从CoroutineScope派生类,并使用要启动{}的类。- doSomething_1().
这两者之间有什么区别,哪种方式更好呢?
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()
}
}发布于 2020-08-01 07:58:24
这两者之间有什么区别,哪种方式更好呢?
是的,有一个基本的区别,使一个正确和另一个不正确。它是关于结构化并发的:如果您的AClass是您的“工作单元”(不管是什么)的根对象,并且是它生命周期的负责人(或观察者),那么它也应该是您将在其中启动的coroutine的根范围。当生命周期结束时,AClass应该通过对自身调用cancel来将该事件传播到协同子系统,从而取消根范围。CoroutineScope.cancel是一个扩展函数。
我接受了您的代码并做了以下修正:
CoroutineScope.coroutineContext必须有一个Job()在里面,所以我添加了它。我删除了dispatcher,因为它与这个故事无关,而Main调度器是用于GUI的,而我们正在运行一个简单的测试。dispose()函数,我们有cancel()开箱即用。theJob1和theJob2字段,因为一旦您开始正确地使用结构化并发,它们就没有用了。我还添加了一些允许我们观察行为的代码:
delay,并添加一个println来查看何时完成。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) {发布于 2020-08-01 02:54:21
这两个合作机制将在平等的背景下启动。您可以通过在这两个文件中打印coroutine上下文来看到这一点:
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一个新的协同线时,现有的上下文与您提供的新上下文简单地结合在一起。
进一步读:
https://stackoverflow.com/questions/63196192
复制相似问题