ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[译] 协程中的取消和异常(第 1 部分)- 协程:第一件事

2021-06-17 15:30:48  阅读:268  来源: 互联网

标签:协程 父级 取消 Job CoroutineScope CoroutineContext 第一件 scope


原文:https://medium.com/androiddevelopers/coroutines-first-things-first-e6187bf3bb21

本系列博客文章深入探讨了协程中的取消和异常。取消对于避免做多余的工作很重要,这会浪费内存和电量;正确的异常处理是良好用户体验的关键。作为本系列其他 3 部分(第 2 部分:取消,第 3 部分:异常,第 4 部分:不应取消的工作的协程和模式)的基础,定义一些核心协程概念非常重要,例如 CoroutineScope, Job 和 CoroutineContext 以便我们都在同一页面上。

CoroutineScope 协程作用域

一个 CoroutineScope 跟踪您使用 launch 或 async 创建的任何协程(这些是 CoroutineScope 上的扩展函数)。可以通过 scope.cancel() 在任何时间点调用来取消正在进行的工作(正在运行的协程)。
每当您想启动和控制应用程序特定层中协程的生命周期时,您都应该创建一个 CoroutineScope。在一些像 Android 这样的平台上,有一些 KTX 库已经在某些生命周期类中提供了一个 CoroutineScope,比如 viewModelScope 和 lifecycleScope。
创建一个 CoroutineScope 时,它将一个 CoroutineContext 作为其构造函数的参数。您可以使用以下代码创建一个新的 scope 作用域和 coroutine 协程:

// Job and Dispatcher are combined into a CoroutineContext which
// will be discussed shortly
val scope = CoroutineScope(Job() + Dispatchers.Main)
val job = scope.launch {
    // new coroutine
}

Job 工作任务

一个 Job 是协程的句柄。对于您(通过 launch 或 async)创建的每个协程,它返回一个唯一标识协程并管理其生命周期的 Job 实例。正如我们在上面看到的,您还可以将一个 Job 传递给一个 CoroutineScope 以保持对其生命周期的句柄。

CoroutineContext 协程上下文

CoroutineContext 是一组定义协程行为的元素。 它由以下组成:

  • Job — 控制协程的生命周期。
  • CoroutineDispatcher — 将工作分派到适当的线程。
  • CoroutineName — 协程的名称,用于调试。
  • CoroutineExceptionHandler — 处理未捕获的异常,将在本系列的第 3 部分中介绍。

什么是新的协程的 CoroutineContext ?我们已经知道将创建一个新 Job 实例,允许我们控制它的生命周期。其余元素将继承其父级(另一个协程或创建它的 CoroutineScope)的 CoroutineContext。
由于一个 CoroutineScope 可以创建协程并且您可以在协程内创建更多协程,因此创建了隐式任务层次结构。在下面的代码片段中,除了使用 CoroutineScope 来创建一个新的协程,看看如何在一个协程中创建更多的协程:

val scope = CoroutineScope(Job() + Dispatchers.Main)
val job = scope.launch {
    // New coroutine that has CoroutineScope as a parent
    val result = async {
        // New coroutine that has the coroutine started by 
        // launch as a parent
    }.await()
}

该层次结构的根通常是 CoroutineScope。我们可以将层次结构可视化如下:
在这里插入图片描述
协程在任务层次结构中执行。父级可以是一个 CoroutineScope 或另一个协程。

Job 生命周期

一个 Job 可以经历一组状态:New、Active、Completing、Completed、Canceling 和 Cancelled。虽然我们没有权限访问状态本身,我们可以访问 Job 的属性:isActive,isCancelled 和 isCompleted。
在这里插入图片描述
作业生命周期

如果协程处于活动状态,则协程的失败或 job.cancel() 调用会将作业移至取消状态 ( isActive = false, isCancelled = true)。一旦所有孩子都完成了他们的工作,协程将进入 Canceled 状态并且 isCompleted = true。

父 CoroutineContext 说明

在任务层次结构中,每个协程都有一个父协程,可以是一个 CoroutineScope 协程或另一个协程。但是,一个协程的 CoroutineContext 父级可能与父级(协程)的 CoroutineContext 的不同,因为它是根据以下公式计算的:

Parent context = Defaults + inherited CoroutineContext + arguments

在这里:

  • 某些元素具有默认值:Dispatchers.Default 是 CoroutineDispatcher 的默认值,“coroutine” 是 CoroutineName 的默认值。
  • 继承的 CoroutineContext 是 CoroutineScope 的或者创建它的协程的 CoroutineContext。
  • 在协程构建器中传递的参数将优先于继承上下文中的那些元素。

注意:CoroutineContext 可以使用+运算符组合 。由于 CoroutineContext 是一组元素,因此将使用加号右侧的元素覆盖左侧的元素来创建一个新的 CoroutineContext。例如

(Dispatchers.Main, “name”) + (Dispatchers.IO) = (Dispatchers.IO, “name”)

在这里插入图片描述
这个 CoroutineScope 启动的每个协程至少在 CoroutineContext 中有这些元素。CoroutineName 是灰色的,因为它来自默认值。

现在我们知道一个新协程的父级 CoroutineContext 是什么,它的实际 CoroutineContext 将是:

New coroutine context = parent CoroutineContext + Job()

如果以如上图所示的 CoroutineScope,我们像如下创建一个新的协程:

val job = scope.launch(Dispatchers.IO) {
    // new coroutine
}

该协程的父级 CoroutineContext 及其实际的 CoroutineContext 是什么?请参阅下图中的解决方案!
在这里插入图片描述
CoroutineContext 中的和父级 CoroutineContext 中的 Job 永远不会是同一个实例,因为新的协程总是得到一个 Job 的新实例

父级 CoroutineContext 有 Dispatchers.IO 而不是 scope 的 CoroutineDispatcher,因为它被协程构建器的参数覆盖。此外,检查父级 CoroutineContext 中的 Job 是 scope 的 Job(红色)的实例,并且一个新的 Job(绿色)实例已分配给新协程的实际 CoroutineContext。

正如我们将在本系列的第 3 部分中看到的, 一个 CoroutineScope 在它的 CoroutineContext 中可以有一个不同的 Job 实现,称作 SupervisorJob,它改变了 CoroutineScope 处理异常的方式。因此,使用该 scope 创建的新协程可以以 SupervisorJob 作为父级 Job。但是,当一个协程的父协程是另一个协程时,父级 Job 将始终是 Job 类型。

标签:协程,父级,取消,Job,CoroutineScope,CoroutineContext,第一件,scope
来源: https://blog.csdn.net/hegan2010/article/details/117992423

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有