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

Posted

技术标签:

【中文标题】Kotlin:协程范围与协程上下文【英文标题】:Kotlin: Coroutines scope vs Coroutine context 【发布时间】:2019-06-22 07:58:12 【问题描述】:

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

【问题讨论】:

您可以阅读本文了解更多详情。 medium.com/@elizarov/coroutine-context-and-scope-c8b255d59055 @mallaudin 我认为这应该是一个公认的答案。 谢谢@mallaudin。 【参考方案1】:

它们确实密切相关。你可能会说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 并且也会取消 launched 作业。

请注意关键的句法特征:我明确写了scope0.launch,但如果我只写launch,则暗示的意思完全一样。这就是CoroutineScope 帮助“自动”传播作用域的方式。

【讨论】:

【参考方案2】:

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

范围

协程必须在作用域内运行 这是一种跟踪其中运行的所有协程的方法 所有 (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

【讨论】:

我认为当您说上下文有四个选项时,您的意思是您可以通过上下文参数传递的 Dispatcher 有四个选项。该参数可用于调度程序以外的其他事情,例如作业或协程名称。这些都不是上下文本身。它们是上下文的元素(用于扩充父上下文)。【参考方案3】:

CoroutineScope 有一个CoroutineContext

例如,如果您有:

runBlocking  // defines coroutineScope

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

    

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

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

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

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

【讨论】:

以上是关于Kotlin:协程范围与协程上下文的主要内容,如果未能解决你的问题,请参考以下文章

线程进程与协程

Kotlin 协程协程简介 ( 协程概念 | 协程作用 | 创建 Android 工程并进行协程相关配置开发 | 异步任务与协程对比 )

Kotlin 协程协程简介 ( 协程概念 | 协程作用 | 创建 Android 工程并进行协程相关配置开发 | 异步任务与协程对比 )

闭包(closure)与协程共用时要注意的事情

Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )

Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )