简体   繁体   English

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

[英]Kotlin: Coroutines scope vs Coroutine context

Can anyone explain the difference between them?谁能解释一下它们之间的区别? I think scope provides a reference(eg Job) to cancel them and context provides a reference to underlying thread.我认为 scope 提供了一个参考(例如 Job)来取消它们,而 context 提供了对底层线程的引用。 Is that so?是这样吗?

They are indeed closely related.它们确实是密切相关的。 You might say that CoroutineScope formalizes the way the CoroutineContext is inherited.你可能会说CoroutineScope形式化了CoroutineContext的继承方式。

CoroutineScope has no data on its own, it just holds a CoroutineContext . CoroutineScope本身没有数据,它只有一个CoroutineContext Its key role is as the implicit receiver of the block you pass to launch , async etc.它的关键作用是作为传递给launchasync等的块的隐式接收器。

See this example:看这个例子:

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
        }
    }
}

You can see how the CoroutineScope mediates the inheritance of coroutine contexts.您可以看到CoroutineScope如何调解协程上下文的继承。 If you cancel the job in scope1 , this will propagate to scope2 and will cancel the launch ed job as well.如果您取消scope1的作业,这将传播到scope2并且也将取消launch作业。

Note the key syntactical feature: I explicitly wrote scope0.launch , but had I written just launch , it would implicitly mean exactly the same thing.请注意关键的语法特征:我明确地编写了scope0.launch ,但如果我只编写了launch ,它会隐含地表示完全相同的东西。 This is how CoroutineScope helps to "automatically" propagate the scope.这就是CoroutineScope如何帮助“自动”传播范围。

Yes, in principle you are right, here more details.是的,原则上你是对的,这里有更多细节。

Scope范围

  • a coroutine must run in a scope协程必须在一个范围内运行
  • it is a way to keep track of all coroutines that run in it这是一种跟踪在其中运行的所有协程的方法
  • all ( cooperative ) coroutines can be cancelled via their scope所有( 合作)协程都可以通过其作用域取消
  • scopes get uncaught exceptions范围获得未捕获的异常
  • they are a way to bind coroutines to an application specific lifecycle (eg viewModelScope in Android) to avoid leaking它们是一种将协程绑定到特定于应用程序的生命周期(例如 Android 中的viewModelScope )以避免泄漏的方法

Context语境

The context determines on which thread the coroutines will run.上下文决定协程将在哪个线程上运行。 There are four options:有四个选项:

  • Dispatchers.Default - for CPU intense work (eg sorting a big list) Dispatchers.Default - 用于 CPU 密集型工作(例如对大列表进行排序)
  • Dispatchers.Main - what this will be depends on what you've added to your programs runtime dependencies (eg kotlinx-coroutines-android , for the UI thread in Android) Dispatchers.Main - 这将取决于您添加到程序运行时依赖项中的内容(例如kotlinx-coroutines-android ,用于kotlinx-coroutines-android中的 UI 线程)
  • Dispatchers.Unconfined - runs coroutines unconfined on no specific thread Dispatchers.Unconfined - 在没有特定线程的情况下不受限制地运行协程
  • Dispatchers.IO - for heavy IO work (eg long-running database queries) Dispatchers.IO - 用于繁重的 IO 工作(例如长时间运行的数据库查询)

The following example brings both scope and context together.以下示例将范围和上下文结合在一起。 It creates a new scope in which the coroutines will run (if not changed) on a thread designated for IO work and cancels them via their scope.它创建了一个新的作用域,在该作用域中,协程将在指定用于 IO 工作的线程上运行(如果未更改),并通过其作用域取消它们。

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

CoroutineScope has-a CoroutineContext . CoroutineScope有一个CoroutineContext

For example if you have:例如,如果您有:

runBlocking { // defines coroutineScope

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

    }
}

runBlocking defines a CoroutineScope (learn about it here ) which launch inherits. runBlocking定义了一个launch继承的CoroutineScope在这里了解它)。 The context is being overridden by explicitly specifying a dispatcher here.通过在此处显式指定调度程序来覆盖上下文。 If you look at the definition of launch , you can see that it takes an optional CoroutineContext :如果您查看launch的定义,您会发现它需要一个可选的CoroutineContext

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

Another part of the context would be the coroutine's name:上下文的另一部分是协程的名称:

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

Scope 范围

Every coroutine builder (like launch, async, etc) and every scoping function (like coroutineScope, withContext, etc) provides its own scope with its own Job instance into the inner block of code it runs. 每个协同程序构建器(如启动,异步等)和每个作用域函数(如coroutineScope,withContext等)都会将自己的作用域实例提供给它运行的内部代码块。 By convention, they all wait for all the coroutines inside their block to complete before completing themselves, thus enforcing the discipline of structured concurrency. 按照惯例,它们都会等待块内的所有协同程序在完成自己之前完成,从而强制执行结构化并发规则。

Source 资源

Context 上下文

Coroutines always execute in some context which is represented by the value of CoroutineContext type, defined in the Kotlin standard library. 协同程序总是在某些上下文中执行,该上下文由在Kotlin标准库中定义的CoroutineContext类型的值表示。

The coroutine context is a set of various elements. 协程上下文是一组各种元素。 The main elements are the Job of the coroutine, which we've seen before, and its dispatcher, which is covered in this section. 主要元素是我们之前见过的coroutine的Job和它的调度程序,本节将对其进行介绍。

Source 资源

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM