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

时间:2019-01-29 08:37:03

标签: kotlin kotlin-coroutines

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

4 个答案:

答案 0 :(得分:10)

是的,原则上您是对的,这里有更多详细信息。

范围

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

上下文

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

  • 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

答案 1 :(得分:2)

它们确实息息相关。您可能会说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
        }
    }
}

您可以看到CoroutineScope如何介导协程上下文的继承。如果您在scope1中取消作业,则该作业将传播到scope2,也将取消已launch编辑的作业。

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

答案 2 :(得分:1)

范围

  

每个协程生成器(例如启动,异步等)和每个作用域   函数(例如coroutineScope,withContext等)提供了自己的功能   作用域及其自己的Job实例进入其运行的内部代码块。   按照惯例,它们都在等待它们块中的所有协程   在完成自己之前完成,从而强制执行   结构化并发学科。

Source

上下文

  

协程总是在某些情况下执行,这由   在Kotlin标准中定义的CoroutineContext类型的值   库。

     

协程上下文是一组各种元素。主要要素   是我们之前所见过的协程的工作   调度程序,本节将对此进行介绍。

Source

答案 3 :(得分:1)

CoroutineScope具有-CoroutineContext

例如,如果您拥有:

runBlocking { // defines coroutineScope

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

    }
}

runBlocking定义了CoroutineScope继承的launch(了解here)。通过在此处明确指定调度程序,可以覆盖上下文。如果查看launch的定义,您会发现它需要一个可选的CoroutineContext

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    ...
)

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

launch(CoroutineName("launchMe") + Dispatchers.Default) {
    println("")
}