Kotlin:协程范围与协程上下文

Jos*_*ose 8 kotlin kotlin-coroutines

谁能解释他们之间的区别?我认为范围提供了一个引用(例如Job)来取消它们,上下文提供了对底层线程的引用。是这样吗?

Wil*_*zel 26

是的,原则上你是对的,这里有更多细节。

范围

  • 协程必须在一个范围内运行
  • 这是一种跟踪在其中运行的所有协程的方法
  • 所有(合作)协程都可以通过其作用域取消
  • 范围获得未捕获的异常
  • 它们是一种将协程绑定到特定于应用程序的生命周期(例如viewModelScope在 Android 中)以避免泄漏的方法

语境

上下文决定协程将在哪个线程上运行。有四个选项:

  • Dispatchers.Default - 用于 CPU 密集型工作(例如对大列表进行排序)
  • Dispatchers.Main- 这将取决于您添加到程序运行时依赖项中的内容(例如kotlinx-coroutines-android,对于 Android 中的 UI 线程)
  • Dispatchers.Unconfined - 在没有特定线程的情况下运行无限制的协程
  • Dispatchers.IO - 用于繁重的 IO 工作(例如长时间运行的数据库查询)

以下示例将范围和上下文结合在一起。它创建了一个新的作用域,在该作用域中,协程将在指定用于 IO 工作的线程上运行(如果未更改),并通过其作用域取消它们。

val scope = CoroutineScope(context = Dispatchers.IO) 
val job = scope.launch {
    val result = suspendFunc1()
    suspendFunc2(result)
}
// ...
scope.cancel() // suspendFunc1() and suspendFunc2() will be cancelled
Run Code Online (Sandbox Code Playgroud)

  • 我认为当您说上下文有四个选项时,您的意思是可以通过上下文参数传递的调度程序有四个选项。该参数可用于除调度程序之外的其他内容,例如作业或协程名称。这些本身都不是上下文;它们是上下文的元素(用于增强父上下文)。 (5认同)

s1m*_*nw1 11

CoroutineScope有-a CoroutineContext

例如,如果您有:

runBlocking { // defines coroutineScope

    launch(Dispatchers.Default) { //inherits coroutineScope but changes context

    }
}
Run Code Online (Sandbox Code Playgroud)

runBlocking定义一个CoroutineScope在这里了解它)launch继承。通过在此处显式指定调度程序来覆盖上下文。如果您查看 的定义launch,您会发现它带有一个可选的CoroutineContext

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    ...
)
Run Code Online (Sandbox Code Playgroud)

上下文的另一部分是协程的名称:

launch(CoroutineName("launchMe") + Dispatchers.Default) {
    println("")
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您重新阅读您的答案,您确实可以看到您没有解释任何有关差异的内容 (4认同)

Mar*_*nik 6

它们确实紧密相关。您可能会说,这可以CoroutineScope正式定义CoroutineContext继承的方式。

CoroutineScope本身没有数据,它只保存一个CoroutineContext。它的关键作用是作为传递给的块的隐式接收者launchasync等等。

请参阅以下示例:

runBlocking {
    val scope0 = this
    // scope0 is the top-level coroutine scope.
    scope0.launch {
        val scope1 = this
        // scope1 inherits its context from scope0. It replaces the Job field
        // with its own job, which is a child of the job in scope0.
        // It retains the Dispatcher field so the launched coroutine uses
        // the dispatcher created by runBlocking.
        scope1.launch {
            val scope2 = this
            // scope2 inherits from scope1
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到如何CoroutineScope协调协程上下文的继承。如果您在中取消作业scope1,这将传播到,scope2同时也将取消launched作业。

请注意关键的语法功能:我显式编写了代码scope0.launch,但是如果我只是编写了代码launch,则意味着完全相同。这就是CoroutineScope帮助“自动”传播范围的方式。