Kotlin 协同视野背后的概念是什么?

Kotlin 协同视野背后的概念是什么?,kotlin,kotlinx.coroutines,Kotlin,Kotlinx.coroutines,在阅读了的简介和javadoc之后,我仍然有点困惑CoroutineScope背后的想法是什么 文档的第一句话“为新的协同程序定义一个范围”。我不清楚:为什么我的协同程序需要一个范围 另外,为什么不推荐独立的协同程序构建器?为什么这样做更好: fun CoroutineScope.produceSquares(): ReceiveChannel<Int> = produce { for (x in 1..5) send(x * x) } fun CoroutineScope

在阅读了的简介和javadoc之后,我仍然有点困惑
CoroutineScope
背后的想法是什么

文档的第一句话“为新的协同程序定义一个范围”。我不清楚:为什么我的协同程序需要一个范围

另外,为什么不推荐独立的协同程序构建器?为什么这样做更好:

fun CoroutineScope.produceSquares(): ReceiveChannel<Int> = produce {
    for (x in 1..5) send(x * x)
}
fun CoroutineScope.produceSquares():ReceiveChannel=product{
对于(1..5中的x)发送(x*x)
}
而不是

fun produceSquares(): ReceiveChannel<Int> = produce { //no longer an extension function
    for (x in 1..5) send(x * x)
}
fun-produceSquares():ReceiveChannel=product{//不再是扩展函数
对于(1..5中的x)发送(x*x)
}

它与结构化并发的概念相关,后者定义了协同路由之间的结构

,您很少像使用线程那样“全局”启动协同路由。协同程序总是与应用程序中的某个局部范围相关,这是一个具有有限生命周期的实体,如UI元素。因此,对于结构化并发,我们现在要求在一个CoroutineScope中调用launch,这是一个由生命周期有限的对象(如UI元素或其相应的视图模型)实现的接口

这个概念的一个明显结果是:通过
取消
绑定
范围的上下文,它的所有子目录也将被取消。

您仍然可以通过在
全局范围内生成全局“独立”协程来使用它们:

GlobalScope.launch {
    println("I'm running unstructured")
}
但是,不建议这样做,因为在全局范围上创建协程基本上与我们使用好的旧线程所做的相同。您创建了它们,但不知何故需要跟踪引用,以便以后加入/取消它们

使用结构化并发,即在其作用域中嵌套协同路由,您将拥有一个更易于维护的系统。例如,如果在另一个协同程序中生成一个协同程序,则继承外部作用域。这有多重好处。如果取消外部协程,则取消将委托给其内部协程。此外,您可以确定,在所有子协同程序完成工作之前,外部协同程序不会完成

for
CoroutineScope
中还显示了一个非常好的示例

应该在具有定义良好的生命周期、负责启动子协同路由的实体上实现协同路由。Android上此类实体的示例是活动

毕竟,所显示的
produceSquares
方法的第一个版本更好,因为它只有在
CoroutineScope
中调用时才是可执行的。这意味着您可以在任何其他协同程序中运行它:

launch {
    produceSquares()
}
produceSquares
内部创建的协同程序继承了
launch
的范围。您可以确保在
produceSquares
之前,
launch
不会完成。此外,如果您取消了
启动
,这也会影响
produceSquares

此外,您仍然可以创建一个全局运行的协同程序,如下所示:

GlobalScope.produceSquares()
但是,正如前面提到的,在大多数情况下,这不是最好的选择


我还想宣传我写的一篇文章。有一些示例演示了作用域的含义:

我发现github问题中的描述非常有用:。正如上面所解释的,“…协同程序生成器,如
launch{…}
async{…}
默认启动一个全局协同程序…这似乎是一个错误的默认值。全局协同程序很容易出错。”现在,如果需要,您仍然可以使用
GlobalScope
,执行与旧行为相同的操作,但这意味着你是在明确地做这件事,而不是意外地做这件事。建议您在大多数情况下不要使用
GlobalScope
取消作业的子项。我仍然不确定作用域的实际作业与协同程序的关系。它的生命周期和工作处理?与实际线程的关联(我的意思是,最终,代码必须由线程执行,因此,需要有人以某种方式决定需要执行哪个线程的协同程序)最终是关于协同程序的生命周期。
CoroutineDispatcher
(只是
CoroutineScope
的一部分)定义了要运行的线程。例如,您可以说
launch(Dispatchers.Default)
来指定调度器。否则,您将从外部继承scope@morpheus05不要忘记,同一个协程可以从一个线程传递到另一个线程,它将在哪个线程上运行并没有单一的决定。当它被挂起时,它不会被分配给任何线程,并且每次恢复时都会做出新的决定。您还可以使用
withContext
在不挂起的情况下切换线程。最好使用的心智模型是,协同程序运行的“底层”是一个线程,就像CPU核心是线程运行的底层一样。如果我看一下协同程序,它是一个带有一个字段的接口,coroutineContext:coroutineContext。但为什么会出现这种界面呢?在作用域的生命周期中,上下文是否可以更改?为什么要引入这个接口,假设我们只使用CoroutineContext?我自己写了一个例子()来理解。我想知道,使用
GlobalScope.launch(Dispatchers.IO){…}
而不是
CoroutineScope(Dispatchers.IO).launch{…}
有什么优点/缺点吗?