kotlin协程的理解

news2024/11/17 16:06:47

伴生对象:companion object 其实质等同于Java中的单例模式

协程:通常实现是用户态的任务协作式调度

  1. 一段可执行代码
  2. 可挂起/可恢复执行
  3. 概念上与语言无关,协程这个概念于1958年提出

依赖框架:

协程的启动:

1.协程体:协程中要执行的操作,是一个被suspend修饰的lambda表达式

传递末尾的lambda表达式,在Kotlin中有一个约定:

如果函数的最后一个参数是函数,那么作为相应的参数的传入的lambda表达式可以放在圆括号之外

2.协程体类:编译器会将协程体编译成封装协程体操作的匿名内部类

3.协程构建器:用于构建协程的函数:比如launch,async

4.挂起函数:由suspend修饰的函数,挂起函数只能在挂起函数或者协程体中调用,

5.挂起点:一般对应挂起函数被调用的位置

6.续体:Continuation

CoroutineScope:

  1. 是一个接口
  2. 只有一个属性:CoroutineContext(协程上下文),
  3. 是一个作用范围,可以通过CoroutineScope的扩展函数去创建协程(launch async),当这个作用范围被取消的时候,其内部协程也会被取消;GlobalScope除外
  • launch函数返回一个Job,可以通过Job进行管理协程
  • 为协程提供一个上下文CoroutineContext

GlobalScope:

  • 实现CoroutineScope接口,并且重写了上下文,返回一个EmptyCoroutineContext
  • 由object修饰,是一个单例对象,所以生命周期跟随整个应用,无法通过自身取消内部协程

launch函数的三个参数,也就是启动协程的三要素:

  • CoroutineContext 协程的上下文
  • CoroutineStart 协程的启动模式
  • suspend CoroutineScope.() -> Unit 协程体

CoroutineContext 协程上下文 ,这是一个数据集合接口声明,所包含的元素有:

  • 协程中Job
  • 调度器:CoroutineDispatcher
  • 协程名:CoroutineName
  • ...

CoroutineContext 的数据结构:链表

CombinedContext是CoroutineContext的一个实现类,也是链表中的具体实现节点,节点包含两个元素,

  • element:当前的节点集合元素,
  • left :CoroutineContext类型,指向链表的下一个元素

第四行:函数get,一个由operator修饰的操作符重载,对应"[ ]"操作符,通过key获取Element对象

第七行:函数fold,遍历当前集合的每一个Element,并对每一个元素进行opreator操作,将操作后的结果进行累加,以initial为其实开始累加,最终返回一个新的CoroutineContext 上下文

第十六行:函数plus,由operator修饰操作符重载,对应"+"操作符,合并两个CoroutineContext对象中的元素(这个元素可以是实现CoroutineContext 接口的任何对象:Job、CoroutineDispatcher、CoroutineName等等),将合并后的上下文返回,

从函数Plus中,我们可以清晰的看出,CoroutineContext的数据存储方式是一个链表,链表的每个节点是CombinedContext,并且存在拦截器的情况下,拦截器永远是链表的头结点 ,拦截器使用效率很高,这样可以保证更快的读取到拦截器

每个元素在创建的时候都会生成唯一的Key对象(单例模式),所以元素在添加到集合中时(plus)同类元素都会被最后的一个所覆盖

如果存在想相同的key的Element对象,则对其进程"覆盖"(先从集合中移除要plus的Element对象,返回一个移除后的集合,确保当前集合不包含要添加的元素)

以CombinedContext中的minusKey进行理解:

1.当我们在plus一个CoroutineContext元素时,需要对当前的CoroutineContext集合进行移除操作,

2.由于Key的唯一性,链表中不会存在重复的元素结点!首先从头结点的element中根据要添加元素的key进行查询,如果查询结果不为空,则直接返回left(意味着在这个链表存在要添加的元素,并且是当前结点的element,故而可以直接返回left)

3.如果2中的查询结果为空,则继续调用minusKey(递归)直到满足以下条件,退出递归

  • 移除结点后与移除前的left一样,那么也就意味着链表中不存在要添加的element,所以直接 返回这个链表
  • 移除结点后,链表为空,意味着当前链表只有一个结点,并且该结点中的element与要添加的一样,那么直接返回当前的节点的element
  • 当前链表就是一个空链表,那么将第三行代码中的newleft和elememt重新组合

Element:

1.第40行代码:每一个元素的类型是Element,而它又实现了CoroutineContext接口,所以Element即可以是一个集合中的元素,也可以是一个集合

CoroutineStart 是协程的启动模式,存在以下4种模式:

  • DEFAULT 立即调度,可以在执行前被取消
  • LAZY 需要时才启动,需要start、join等函数触发才可进行调度
  • ATOMIC 立即执行,执行前不可以被取消
  • UNDISPATCHED 立即在当前线程执行,直到遇到第一个挂起点(可能切线程)

协程体:suspend CoroutineScope.() -> Unit

一个lambda表达式,也就是协程中要执行的代码块,即launch函数的代码块;

为什么使用CoroutineScope扩展函数?

上面讲到,在CoroutineScope中只有一个属性,那就是协程上下文;这样我们可以在协程体中访问协程上下文这个对象

-----------------------------------协程中线程的挂起 和 切换----------------------------------------

Dispatchers:调度器,是协程中提供的线程调度器,用来切换线程,指定协程所运行的线程

源码分析:

DisPatchers中提供了4种类型的调度器:

  • Defaul:默认调度器,适合CPU密集型任务调度器,比如逻辑计算;
  • Main:UI调度器;
  • Unconfind:无限制(无拘束)调度器,对协程执行的线程不做限制,协程恢复时可以在任意线程;
  • IO:IO调度器,适合IO密集型任务调度器,比如读写文件,网络请求;

从源码中可以看到,这4种类型的调度器的类型均是:CoroutineDispatchers

CoroutineDispatchers:

继承自AbstractCoroutineContextElement,而AbstractCoroutineContextElement是Element的一个抽象实现类,所以调度器本身也是一个CoroutineContext,也可以存放在CoroutineContext集合中;同时实现了ContinuationInterceptor,一个拦截器接口

1.在上图代码中可以看到:ContinuationInterceptor实现了CoroutineContext.Element接口,所以拦截器也可以作为CoroutineContext集合的一个元素

2.在ContinuationInterceptor中定义了一个伴生对象Key,它的类型是CoroutineContext.Key,作为CoroutineContext集合元素的索引的理由:

  • 伴生对象的唯一性
  • 通过类型访问集合元素,更直观

3.interceptContinuation:对协程体类对象continuation的一次包装,并返回一个新的Continuation,

CoroutineDispatcher:继承自AbstractCroutineContextElement,同时实现了拦截器接口;

  • 说明了调度器的本质也是一个拦截器,在kotlin中所有的调度器都是继承自它来实现的自身调度逻辑
  • 调度器同时也可以作为CroutineContext集合中的元素

1.isDispatchNeeded:是否需要线程调度;

2.dispatch:线程调度,让一个runnable对象在指定的线程运行;

3.interceptContinuation:将协程体类对象包装成一个DispatchedContinuation对象;

DispatchedContinuation:使用线程调度器将协程体调度到指定的线程执行

1.实现了续体:Continuation,重写了resumeWith的内部实现逻辑,并且持有线程调度器;

2.有两个属性:

  • dispatcher:线程调度器
  • continuation:线程体类对象,也就是在包装成DispatchedContinuation时传入的协程体类对象

3.关注:delegate,实质就是DispatchedContinuation对象本身

4.resumeWith:首先通过isDispatchNeeded判断是否需要线程调度;

  • 如果需要线程调度,则使用dispatcher#dispatch进行调度,所需要的参数分别是:协程上下文和一个runnable对象(这里传入的this,即表示DispatchedContinuation对象本身,由于其继承自DispatchedTask,继续跟进会发现最终实现了Runnable接口),所以这个runnable会运行在调度的线程上
  • 如果不需要调度,则使用resumeWith,

5.runnable:从下面源码中可以看到,在run方法中首先从delegate中取出协程体对象,然后调用协程体的扩展函数resume,实质还是执行resumeWith

Dispatchers.Default默认调度器

dispatcher#dispatch()的实现是在调度器的具体实现类中,我们以Dispatchers.Default进行分析

1.useCoroutinesScheduler:默认情况是ture,所以会构建一个DefaultScheduler

2.IO调度器是Dispatchers.Default内的一个变量,并且它和Default调度器共享CoroutineScheduler线程池。

3.调度器的核心是重写dispatch()进行线程的切换,追溯到父类的dispatch:

ExperimentalCoroutineDispatcher

1.在ExperimentalCoroutineDispatcher中的dispatch的实现是通过调用coroutineScheduler.dispatch(),

2.CoroutineScheduler是一个Kotlin实现的线程池,提供协程运行的线程。

-----------------------------------协程中Worker线程----------------------------------------

Worker存在5种状态:

  • CPU_ACQUIRED 获取到cpu权限
  • BLOCKING 正在执行IO阻塞任务
  • PARKING 已处理完所有任务,线程挂起
  • DORMANT 初始态
  • TERMINATED 终止态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1896763.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SVN的使用-源代码管理工具-命令行的使用 Objective-C语言】

一、接下来,我们来说一个终端的命令行的使用, 1.我们说,你的电脑里边呢,有终端, 在Mac里边,你想新建一个txt,应该怎么写,对,打开文本编辑, 打开这个东西,写点儿东西,然后保存一下,保存的时候,你还要去选择格式, 现在,如果我们用命令行,可以更方便一些, 2.首…

SCT612404通道,高效高集成,摄像头模组电源集成芯片

集成三路降压变换器,1CH高压BUCK,2CH低压Buck >HVBuck1:输入电压4.0V-20V,输出电流1.2A,Voo300mV/500mV >LVBuck2:输入电压2.7V-5V,输出电流0.6A , 固定1.8V输出 ;LVBuck3:输λ2.7V-5V,输出电流1.2A,可设定固定输出: 1 . 1 V / 1 . 2 V / 1 . 3 …

vite项目配置svg图标(vite-plugin-svg-icons)

1.插件地址 网址 , 可以去里面查看中文文档,里面有详情的教程 2.使用, 如果你安装的有element-plus ,可以使用这样的方式来修改大小和颜色 <el-icon size"18" color"red"><SvgIcon name"xing"></SvgIcon></el-icon> …

MViT(ICCV 2021, Meta)论文解读

paper&#xff1a;Multiscale Vision Transformers official implementation&#xff1a;https://github.com/facebookresearch/SlowFast 背景和出发点 这篇文章提出了多尺度视觉Transformer&#xff08;Multiscale Vision Transformers, MViT&#xff09;的概念&#xff0c…

nftables(1)基本原理

简介 nftables 是 Linux 内核中用于数据包分类的现代框架&#xff0c;用来替代旧的 iptables&#xff08;包括 ip6tables, arptables, ebtables 等&#xff0c;统称为 xtables&#xff09;架构。nftables 提供了更强大、更灵活以及更易于管理的规则集配置方式&#xff0c;使得…

中国1km高分辨率高质量逐年近地表CO数据集(2013-2022年)

该数据为中国高分辨率高质量逐年CO数据集&#xff0c;该数据集主要的空间范围覆盖整个中国&#xff0c;其中内容包括中国1km高分辨率高质量逐年CO数据集(2013-2022年)。时间分辨率为年&#xff0c;单位为mg/m3&#xff0c;数据以(.nc/.tif)格式进行存储。

Vscode快捷键崩溃

Vscode快捷键崩溃 Linux虚拟机下使用vscode写代码【ctrlA&#xff0c;CtrlC&#xff0c;CtrlV】等快捷键都不能使用&#xff0c;还会出现“NO text insert“等抽象的指令&#xff0c;问题就是不知道什么时候装了一个VIM插件&#xff0c;让他滚出电脑》》》

快速傅里叶变换(Fast Fourier Transform)

快速算法&#xff08;FFT&#xff09;&#xff0c;即快速傅里叶变换&#xff08;Fast Fourier Transform&#xff09;&#xff0c;是一种用于计算离散傅里叶变换&#xff08;DFT&#xff09;及其逆变换的高效算法。FFT算法由J.W.库利和T.W.图基于1965年提出&#xff0c;显著减少…

T100-XG查询报表的开发

制作XG报表 1、注册程序 azzi900 首先现将程序注册一下,在内部构建基础代码档。 2、注册作业 azzi910 也是直接新增一个,作业跟程序绑定一下。 3、T100签出规格程序 这个时候应该是没签出的,首先将规格迁出。 4、T100画面产生器 规格迁出之后,这个时候还需要生成一个画…

【探索Linux】P.37(传输层 —— TCP协议通信机制 | 确认应答(ACK)机制 | 超时重传机制)

阅读导航 引言一、确认应答(ACK)机制1. 成功接收2. 过程中存在丢包3. 引入序列号&#xff08;1&#xff09;序列号的定义&#xff08;2&#xff09;序列号的作用&#xff08;3&#xff09;序列号的工作原理&#xff08;4&#xff09;序列号和确认应答号 二、超时重传机制1. 超时…

Linux/Ubuntu访问局域网共享文件夹

文件夹中找到“Other Location”&#xff0c;输入“smb:IP地址/共享文件夹名称”&#xff0c;然后点击connect后者直接回车即可&#xff01; End&#xff01;

【毛发教程】使用 Maya、XGen 和虚幻引擎创建马尾辫发型

Malte Resenberger-Loosmann是国外一名首席艺术家&#xff0c;他负责指导整个艺术部门来制作独立游戏项目中的3D建模。在本文中&#xff0c;Loosmann展示了马尾辫发型背后的工作流程&#xff0c;分享了 Maya 和虚幻引擎中的场景设置&#xff0c;并解释了 GS CurveTools 如何帮助…

RTL8211FSI PHY电路设计

文章目录 硬件设计引脚功能框图说明PHYADDRPageLED 模式自动协商/速度/全半双工模式Soft Reset上电顺序 原理图设计参考 软件控制&#xff08;FPGA&#xff09;硬件调试 硬件设计 引脚 笔者前代数字采集板采用的 PHY 芯片是博通 Boardcom 的 B50610&#xff0c;其仅支持 0 ∼…

从零到一:eBay自养号测评全流程解析与实操建议

eBay自养号测评是一种通过模拟真实买家行为&#xff0c;为卖家提供市场反馈并提升店铺权重和排名的技术手段。以下是进行eBay自养号测评的具体步骤和注意事项&#xff1a; 一、准备阶段 1. 技术配置&#xff1a;搭建境外服务器&#xff1a;选择稳定的境外服务器&#xff0c;模…

【解码现代 C++】:实现自己的智能 【String 类】

目录 1. 经典的String类问题 1.1 构造函数 小李的理解 1.2 析构函数 小李的理解 1.3 测试函数 小李的理解 1.4 需要记住的知识点 2. 浅拷贝 2.1 什么是浅拷贝 小李的理解 2.2 需要记住的知识点 3. 深拷贝 3.1 传统版写法的String类 3.1.1 拷贝构造函数 小李的理…

go zero入门

一、goctl安装 goctl 是 go-zero 的内置脚手架&#xff0c;可以一键生成代码、文档、部署 k8s yaml、dockerfile 等。 # Go 1.16 及以后版本 go install github.com/zeromicro/go-zero/tools/goctllatest检查是否安装成功 $ goctl -v goctl version 1.6.6 darwin/amd64vscod…

0/1背包问题总结

文章目录 &#x1f347;什么是0/1背包问题&#xff1f;&#x1f348;例题&#x1f349;1.分割等和子集&#x1f349;2.目标和&#x1f349;3.最后一块石头的重量Ⅱ &#x1f34a;总结 博客主页&#xff1a;lyyyyrics &#x1f347;什么是0/1背包问题&#xff1f; 0/1背包问题是…

html三级菜单

示例 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Menu Example</title> <link re…

加速度传感器信号处理注意事项

1 传感器分类 对于压电式压力传感器而言&#xff0c;输出信号是最重要的选择标准之一。压电式压力传感器与电子电路相连&#xff0c;电子电路将传感器产生的电荷成比例转换为电压。 如果选用外部设备&#xff08;电荷放大器&#xff09;充当电子元件&#xff0c;则称其为电…

MYSQL篇二:数据库的操作

文章目录 1. 创建数据库1.1 查看数据库列表1.2 创建与删除数据库 2. 数据的编码问题3. 字符集和校验规则3.1 查看系统默认字符集以及校验规则3.2 查看数据库支持的字符集3.3 查看数据库支持的字符集校验规则3.4 校验规则对数据库的影响 4. 操纵数据库4.1 查看当前是哪一个数据库…