不用休眠的 Kotlin 并发:深入对比 delay() 和 sleep()

在这里插入图片描述

本文翻译自:
https://blog.shreyaspatil.dev/sleepless-concurrency-delay-vs-threadsleep

毫无疑问,Kotlin 语言中的协程 Coroutine 极大地帮助了开发者更加容易地处理异步编程。该特性中封装的诸多高效 API,可以确保开发者花费更小的精力去完成并发任务。一般来说,开发者了解一下如何使用这些 API 就足够了!

可就 JVM 的角度而言,协程一定程度上减少了*“回调地狱”*的问题,切实地改进了异步处理的编码方式。

相信包括笔者在内的很多开发者常常会好奇协程的背后到底是如何做到的。所以,本文将以 delay() 为切入点,带开发者剖析下协程的背后原理。

目录前瞻:

  1. delay() 干啥用的?
  2. sleep() 呢?
  3. 对比 delay() 和 sleep()
  4. 剖析 delay() 原理

1. delay() 干啥用的?

使用过协程的开发者大概率对 delay() 并不陌生,anyway,先来看下官方针对该函数的描述:

“delay() 用来延迟协程一段时间,但不阻塞线程,并且能在指定的时间后恢复协程的执行。”

来看一段在 task1 执行 2000ms 后执行 task2 的示例代码:

scope.launch {doTask1()delay(2000)doTask2()
}

代码很简单,但需要再次提醒一些关于 delay() 的重要特点:

  • 它不会阻塞当前运行的线程
  • 但它允许其他协程在同线程运行
  • 当延迟的时间到了,协程会被恢复并继续执行

很多开发者常常会将 delay() 和 Java 语言的 sleep() 进行比较。可事实上,这两个函数用作完全不同的场景,只是命名上看起来有点相似而已。。。

2. sleep() 呢?

sleep() 则是 Java 语言中标准的多线程处理 API:促使当前执行的线程进入休眠,并持续指定的一段时间

“该方法一般用来告知 CPU 让出处理时间给 App 的其他线程或者其他 App 的线程。”

如果在协程里使用该函数,它会导致当前运行的线程被阻塞,同时也会导致该线程的其他协程被阻塞,直到指定的阻塞时间完成。

为了解更多的细节,让我们通过示例进一步地对比 sleep() 和 delay() 两者。

3. 对比 delay() 和 sleep()

假使我们想在单线程(就比如 Android 开发里的主线程)里执行并发任务。

看一下如下的代码片段:分别启动两个协程,并各自调用了 1000ms 的 delay() 或 sleep()。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

比较:

  • 协程的启动时间:
    • 调用 delay() 代码里的两个协程在同一时间(05:48:58)执行
    • 调用 sleep() 代码里的第 2 个协程相隔了 1s 后执行
  • 协程的结束时间:
    • 调用 delay() 代码里的 2 个协程一共花了 1045ms
    • 调用 sleep() 代码里的 2 个协程则一共花了 2044ms

这也印证了上面提到的特性差异:delay() 只是挂起协程、同时允许其他协程复用该协程,而 sleep() 则在一段时间内直接阻塞了整个线程。

事实上,delay() 还具备其他神奇的特点,再来看看下面的代码示例:

  1. 先定义了一个最大创建 2 个线程的线程池 context 示例

  2. 当第 1 个协程启动并执行一个 task 之后,调用 delay() 挂起 1000ms,接着再执行一个 task

  3. 在第 1 个协程执行的同时,启动第 2 个协程兵执行耗时 task

img

通过查看 task 里打印的 log,我们惊讶地发现:delay 函数执行前,它运行在 Duet-1 线程。但当 delay 完成后,它却恢复到了另一个线程:Duet-2

这是为什么?

原来是因为原线程正在忙于处理第 2 个协程启动的耗时 task,所以 delay 之后它只能恢复到另一个线程。

这就有意思了,看看官方文档的描述。。。

“协程可以挂起一个 thread 并且恢复到另一个 thread!”

既然感受到了 delay() 的魔力,我们就来了解下它背后的工作原理。

4. 剖析 delay() 原理

delay() 会先在协程上下文里找到 Delay 的实现,接着执行具体的延时处理。

public suspend fun delay(timeMillis: Long) {if (timeMillis <= 0) returnreturn suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->if (timeMillis < Long.MAX_VALUE) {cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)}}
}

Delay 是 interface 类型,其定义了延时之后调度协程的方法 scheduleResumeAfterDelay() 等。开发者直接调用的 delay()、withTimeout() 正是 Delay 接口提供的支持。

public interface Delay {public fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>)public fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle =DefaultDelay.invokeOnTimeout(timeMillis, block, context)
}

事实上,Delay 接口由运行协程的各 CoroutineDispatcher 实现。

我们知道 CoroutineDispatcher 是抽象类,Dispatchers 类会利用线程相关 API 来实现它。

比如:

  • Dispatchers.DefaultDispatchers.IO 使用 java.util.concurrent 包下的 Executor API 来实现
  • Dispatchers.Main 使用 Android 平台上特有的 Handler API 来实现

接着,各 Dispatcher 还需要实现 Delay 接口,主要就是实现 scheduleResumeAfterDelay() ,去返回指定 ms 之后执行协程的 Continuation 实例。

如下是 ExecutorCoroutineDispatcherImpl 类实现该方法的具体代码:

override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {(executor as? ScheduledExecutorService)?.scheduleBlock(ResumeUndispatchedRunnable(this, continuation),continuation.context,timeMillis)// Other implementation 
}

可以看到:它借助了 Java 包 ScheduledExecutorServiceschedule() 来调度了 Continuation 的恢复。

我们再来看下 Android 平台 Dispatcher 即 HandlerDispatcher 又是如何实现的该方法。

override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {val block = Runnable {with(continuation) { resumeUndispatched(Unit) }}handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))// Other implementation 
}

它直截了当地使用了 Handler 的 postDelayed() post 了 Continuation 恢复的 Runnable 对象。这也解释了 delay() 没有阻塞线程的原因。

假使你在 Android 主线程的协程里执行了 delay() 逻辑,其效果等同于调用了 Handler 的右侧代码。

img

这种实现非常有趣:在 Android 平台上调用 delay(),实际上相当于通过 Handler post 一个 delayed runnable;而在 JVM 平台上则是利用 Executor API 这种类似的思路。

但如果还是同样的业务逻辑,将 delay() 换成 sleep(),那么效果将大相径庭。可以说,delay() 和 sleep() 是完全不同的两种 API,不要搞混了。

讲到这里,我们能感受到协程的优雅奇妙:用简单的同步代码写出异步逻辑,切实地帮助开发者免受“回调地狱”的困扰。

希望本文能帮你了解到 Kotlin 协程里 delay() 的用法和工作原理,并理解和 sleep() 的明显差异,感谢阅读😃。

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

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

相关文章

Mysql--内置函数

字符串函数 1、拼接字符串 concat(str1,str2...) select concat(12,34,abccc) select CONCAT(name,的家乡是,hometown) from students 2、包含字符个数 length(abc) 注&#xff1a;一个中文占3个字符&#xff0c;一个字母或数字占1个字符 3、截取字符串 left(str,len)返回字…

Elasticsearch:使用 huggingface 模型的 NLP 文本搜索

本博文使用由 Elastic 博客 title 组成的简单数据集在 Elasticsearch 中实现 NLP 文本搜索。你将为博客文档建立索引&#xff0c;并使用摄取管道生成文本嵌入。 通过使用 NLP 模型&#xff0c;你将使用自然语言在博客文档上查询文档。 安装 Elasticsearch 及 Kibana 如果你还没…

计算机竞赛 题目:基于深度学习的中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分&#xff1a;4.2 损失函数&#xff1a;4.3 搭建seq2seq框架&#xff1a;4.4 测试部分&#xff1a;4.5 评价NLP测试效果&#xff1a;4.6 梯度截断…

Safran与是德科技合作为蔚来提供电动汽车中的5G和C-V2X连接测试

概述 虹科Safran GNSS模拟器助力是德科技&#xff08;Keysight&#xff09;为中国顶级电动汽车制造商之一——蔚来汽车&#xff08;NIO&#xff09;提供了业界领先的UXM 5G测试解决方案以验证5G和C-V2X的连接性&#xff0c;能够根据3GPP和其他标准组织定义的最新5G新无线电&am…

解决扬声器异常

之前使用的是PulseAudio PulseAudio 是默认的音频服务器和音频框架&#xff0c;因此大多数应用程序通过 PulseAudio 来处理音频 但也有一些应用程序直接使用 ALSA&#xff08;Advanced Linux Sound Architecture&#xff09;来与音频硬件交互。在这些情况下&#xff0c;ALSA …

深入理解计算机系统(1):系统组成

一、系统硬件组成 1、控制器&#xff08;CPU&#xff09;&#xff1a;解释和执行内存中的指令 &#xff08;1&#xff09;、控制器 程序控制器&#xff1a;指令指针&#xff0c;指向主存中的机器语言指令&#xff0c;为一个字大小的存储设备或寄存器。 指令寄存器、指令译码器、…

离线安装mysql客户端

下载路径 oracle网站总是在不断更新&#xff0c;所以下载位置随时可能变动但万变不离其宗&#xff0c;学习也要学会一通百通。 首先直接搜索&#xff0c;就能找找到mysql官网 打开网站&#xff0c;并点击 DOWNLOADS 往下滚动&#xff0c;找到社区版下载按钮。…

高效解决 TypeError : ‘ numpy._DTypeMeta‘ object is not subscriptable 问题

文章目录 问题描述解决问题 问题描述 解决问题 参考博文 打开报错位置 AppData\Roaming\Python\Python39\site-packages\cv2\typing\ 添加single-quotes&#xff0c;即单引号 博主说The trick is to use single-quotes to avoid the infamous TypeError: ‘numpy._DTypeMeta’…

微信小程序发布流程

前言 上周写了如何写一个小程序&#xff0c;然后经过查资料&#xff0c;改bug&#xff0c;找chatgpt美化页面&#xff0c;我写了一个计算代谢率的小工具&#xff0c;写完了之后该怎么办呢&#xff0c;当然是发布上架&#xff0c;然后我就开始了发布的折腾 提交代码 这一步很…

【uniapp】subnvue组件数据更新视图未更新问题

背景 : 页面中的弹窗使用了subnvue来写, 根据数据依次展示一个一个的弹窗, 点击"关闭"按钮关闭当前弹窗, 显示下一个弹窗 问题 : 当点击关闭时( 使用的splice() ), 数据更新了 , 而视图没有更新, 实际上splice() 是不仅更新数据, 也可以更新视图的 解决 : this.$fo…

WPF中DataContext的绑定技巧

先看效果: 上面的绑定值都是我们自定义的属性,有了以上的提示,那么我们可以轻松绑定字段,再也不用担心错误了。附带源码。 目录 1.建立mvvm项目 2.cs后台使用DataContext绑定 3.xaml前台使用DataContext绑定

selenium查找网页如何处理网站资源一直加载非常卡或者失败的情况

selenium查找网页如何处理网站资源一直加载失败的情况 selenium获取一个网页&#xff0c;某个网页的资源卡了很久还没有加载成功&#xff0c;如何放弃这个卡的数据&#xff0c;继续往下走 有2钟方式。通常可以采用下面的方式一来处理这种情况 方式一、WebDriverWait 这种方式…

差分构造法推广:arc166_d

https://atcoder.jp/contests/arc166/tasks/arc166_d 首先肯定是这样子放&#xff1a; 考虑相邻之间的差&#xff0c;本质就是橙色区间减蓝色区间数量 区间数量越少显然越优&#xff0c;所以我们要么保留橙区间&#xff0c;要么保留紫区间&#xff0c;然后两两匹配 #include…

如何做好sop流程图?sop流程图用什么软件做?

5.如何做好sop流程图&#xff1f;sop流程图用什么软件做&#xff1f; 建立标准作业程序sop已经成为企业进步和发展的必经之路&#xff0c;不过&#xff0c;很多刚刚开始着手搭建sop的企业并不知道要如何操作&#xff0c;对于如何做sop流程图、用什么软件做sop流程图等问题充满…

数据安全防护:云访问安全代理(CASB)

云访问安全代理&#xff08;Cloud Access Security Broker&#xff0c;CASB&#xff09;&#xff0c;是一款面向应用的数据防护服务&#xff0c;基于免应用开发改造的配置方式&#xff0c;提供数据加密、数据脱敏功能。数据加密支持国密算法&#xff0c;提供面向服务侧的字段级…

记录一次springboot使用定时任务中@Async没有生效的场景

环境说明 jdk21springboot 3.0.11 springcloud 2022.0.0 spring-cloud-alibaba 2022.0.0.0 在开发一个定时触发的任务的时候&#xff0c;由于开发执行任务的函数比较耗费时间&#xff0c;所以采用异步解决问题。 发现并没有按照预期的触发 经询问后&#xff0c;发现当前类的…

phpstudy本地域名伪静态

环境&#xff1a;WNMP(Windows10 Nginx1.15.11 MySQL5.7.26 【PHP 7.4.3 (cli) (built: Feb 18 2020 17:29:57) ( NTS Visual C 2017 x64 ) 】) 使用PhpStudy配置本地域名后&#xff0c;设置伪静态&#xff0c;这样在Web端打开网站就不需要输入index.php了&#xff0c;很简单…

竞赛选题 深度学习 python opencv 火焰检测识别 火灾检测

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…

学习记忆——数学篇——案例——代数——均值不等式

文章目录 理解记忆法定义定义推导 重点记忆法用途记忆法使用前提做题应用及易错点两种用法 出题模式法模型识别 谐音记忆法一正二定三相等 秒杀方法 理解记忆法 定义 1.算术平均值&#xff1a;设有n个数 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1​,x2​,...,xn​&#xf…

【LeetCode: 901. 股票价格跨度 | 单调栈】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…