对比 delay() 和 sleep()

Kotlin 语言中的协程 Coroutine 极大地帮助了开发者更加容易地处理异步编程。就 JVM 的角度而言,协程一定程度上减少了  “回调地狱”  的问题,切实地改进了异步处理的编码方式。Coroutine 中封装的诸多高效 API,可以确保开发者花费更小的精力去完成并发任务。今天就来对比一下 Coroutine 中的 delay() 和 Java 语言中的 sleep()。

delay()

如果使用过协程,对于 delay() 必然不陌生,先来看一下官方描述:

Delays coroutine for a given time without blocking a thread and resumes it after a specified time. If the given timeMillis is non-positive, this function returns immediately.

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

用法也很简单,delay() 是 suspend 函数,直接在 CoroutineScope 里调用即可:

lifecycleScope.launch {Log.d(TAG, "1")delay(1000)Log.d(TAG, "2")
}
lifecycleScope.launch {Log.d(TAG, "3")
}

上述代码创建了两个协程,且在第一个协程中使用了 delay(),但是这并不影响第二个协程。因此日志输出结果为:1,3,2,其中1和2两个日志输出时间间隔1秒。

总结一下关于 delay() 的特点:

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

sleep()

sleep() 是 Java 语言中标准的多线程处理 API:促使当前执行的线程进入休眠,并持续指定的一段时间。该方法一般用来告知 CPU 让出处理时间给 App 的其他线程或者其他 App 的线程。

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

对比 delay() 和 sleep()

假使在单线程里执行并发任务。

下面的代码分别启动两个协程,并各自调用了 1000ms 的 delay() 或 sleep()。

lifecycleScope.launch {val totalTime = measureTimeMillis {supervisorScope {launch {Log.d(TAG, "1")delay(1000)
//              Thread.sleep(1000)Log.d(TAG, "2")}launch {Log.d(TAG, "3")delay(1000)
//              Thread.sleep(1000)Log.d(TAG, "4")}}}Log.d(TAG, "totalTime:$totalTime")
}

当调用 delay() 时,两个协程在同一时间执行,先输出日志1和3,经过1秒后,再输出日志2和4,两个协程一共花了 1144 ms。

当调用 sleep() 时,先执行第一个协程输出日志1,经过1秒后,输入日志2,同时执行第二个协程,输出日志3,再经过1秒后,输入之日4,两个协程一共花了 2152 ms。

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

再来看一下 delay() 的其他特点

下面先定义了一个最大创建 2 个线程的线程池 context 示例,然后创建两个协程,并在第一个协程中使用了 delay(),日志输出当前线程名字:

val duetContext = newFixedThreadPoolContext(2, "Duet")
runBlocking(duetContext) {launch {Log.d(TAG, "1-${Thread.currentThread().name}")delay(1000)Log.d(TAG, "2-${Thread.currentThread().name}")}launch {Log.d(TAG, "3-${Thread.currentThread().name}")}
}

我们已经知道日志输出结果为:1,3,2,其中1和2两个日志输出时间间隔1秒。每一个日志输出的当前线程名字是什么呢?

1-Duet-2
3-Duet-1
2-Duet-1

一开始第一个协程在 delay 函数执行前是运行在Duet-2线程的,但当 delay 完成后,它却恢复到了另一个线程:Duet-1。这就是 delay() 的另一个特点:协程可以挂起一个 thread 并且恢复到另一个 thread!

delay() 原理

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

public suspend fun delay(timeMillis: Long) {if (timeMillis <= 0) return // don't delayreturn suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->// if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.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() ,去返回指定毫秒时间之后执行协程的 Continuation 实例。

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

 override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {val future = (executor as? ScheduledExecutorService)?.scheduleBlock(ResumeUndispatchedRunnable(this, continuation),continuation.context,timeMillis)// Other implementation }private fun ScheduledExecutorService.scheduleBlock(block: Runnable, context: CoroutineContext, timeMillis: Long): ScheduledFuture<*>? {return try {schedule(block, timeMillis, TimeUnit.MILLISECONDS)} catch (e: RejectedExecutionException) {cancelJobOnRejection(context, e)null}
}

可以看到借助了 Java 包 ScheduledExecutorService 的 schedule() 来调度了 Continuation 的恢复。

Dispatchers.Main 使用 HandlerDispatcher,看一下 HandlerDispatcher 又是如何实现 scheduleResumeAfterDelay 方法的,具体实现在 HandlerContext 里:

 override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {val block = Runnable {with(continuation) { resumeUndispatched(Unit) }}if (handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))) {continuation.invokeOnCancellation { handler.removeCallbacks(block) }} else {cancelOnRejection(continuation.context, block)}}

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

所以假使在 Android 主线程的协程里执行了 delay() 逻辑,其效果等同于调用了 Handler 的 postDelayed。

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

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

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

相关文章

docker与docker-compose部署

1.1 安装工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm21.2 添加docker的yum库 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum makecache fast1.3 安装Docker sudo yum install docke…

C2prog烧写程序

DSP开发&#xff0c;使用CCS软件建立工程 C2prog 支持C2prog通过串口进行下载&#xff0c;支持out文件。这个下载器下载速度比官方提供的UNIFLASH还要快。 boot跳线设置&#xff0c;不同的DSP拨码方式不相同。制作单板硬件时&#xff0c;通常时将前面三个直接拉到高&#xff…

视频号小店,目前电商界最值得去做的创业项目,一篇解读!

大家好&#xff0c;我是电商小V 要说24年最火的创业性项目那一定是视频号小店&#xff0c;为什么会这样说呢&#xff1f;这主要的还是和他背后的平台有关系&#xff0c;视频号小店是背靠腾讯电商平台的&#xff0c;坐拥的流量可以说是很多的&#xff0c;可以说视频号小店的靠山…

Windows 搭建C++ 纯开源开发环境 进行 YOLOv8 模型推理的开发测试环境

文章大纲 IDE 选择纯开源首选 Codeblocks 跨平台开发IDE其次选择 visual studio 社区版 or visual studio code包管理MSYS2pacmanconda & mambavcpkgNuGetapt-get手动配置 Visual studio 开发环境下载 visual studio基本配置

Modbus通信协议2

一、Modbus概述 1.MODBUS通信栈&#xff08;软件实现方法&#xff09; 2.MODBUS应用协议 2.1 MODBUS应用协议介绍 Modbus是一种简单客户机/服务器应用协议&#xff1b;客户机能够向服务器发送请求&#xff1b;服务器分析请求&#xff0c;处理请求&#xff0c;向客户机发送应…

vue3+cli-service配置代理,跨域请求

一、配置代理端口和代理转发 在vue.config.js文件中 const {defineConfig} require(vue/cli-service)module.exports defineConfig({devServer: {host: 0.0.0.0,port: 8088, // 启动端口号proxy: {/api: { // 请求接口中要替换的标识target: , // 代理地址&#xff0c;后…

金混合纳米粒子催化级联反应产生一氧化碳气体对抗糖尿病牙周炎

引用信息 文 章&#xff1a;Cascade Reactions Catalyzed by Gold Hybrid Nanoparticles Generate CO Gas Against Periodontitis in Diabetes. 期 刊&#xff1a;Advanced Science&#xff08;影响因子&#xff1a;15.1&#xff09; 发表时间&#xff1a;2024年…

汽车IVI中控开发入门及进阶(二十四):杰发科技AC8015

前言: 在此之前的大部分时间,四维图新更多的是以图商的身份在业内出现,但现在四维图新图商之外的技术积累提现在了杰发科技身上,或者是从图商到汽车智能化一体解决方案供应商的角色转变。汽车智能化,可以简单的归为座舱智能化和智能驾驶两个板块。 随着汽车变得越来越智能…

pvt对net delay的影响

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 有星球成员提问: pt中在同一个corner下的net的为啥在min和max的情况下读RC值是不一样的呢??不应该都是根据spef来的吗?? 回答: 这个其实是个误区,相同RC corner情况下我们看report_delay_…

《疯狂python讲义》笔记:类和对象

文章目录 1.类方法classmethod和静态方法staticmethod2.函数装饰器 1.类方法classmethod和静态方法staticmethod 类方法classmethod&#xff1a;第一个参数cls都会被自动绑定到类本身&#xff0c;无论是类还是对象都可调用。 静态方法staticmethod&#xff1a;无论是类还是对象…

SploitScan:一款多功能实用型安全漏洞管理平台

关于SploitScan SploitScan是一款功能完善的实用型网络安全漏洞管理工具&#xff0c;该工具提供了用户友好的界面&#xff0c;旨在简化广大研究人员识别已知安全漏洞的相关信息和复现过程。 SploitScan可以帮助网络安全专业人员快速识别和测试已知安全漏洞&#xff0c;如果你需…

python-web应用程序-Django-From组件

python-web应用程序-Django-From组件 添加用户时 原始方法&#xff08;本质&#xff09;【麻烦】 def user_add(req):if req.method GET:return render(req,XXX.html)#POST请求处理:XXXXX-用户数据没有校验 -出现错误提示 -页面上的每一个字段都需要我们重新写一遍 -关联数…

【运维项目经历|028】Cobbler自动化部署平台构建项目

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

【仿真建模-anylogic】桥式起重机参数化桥架数

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-04 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 问题&#xff1a;anylogic物料库中的桥式起重机只能静态指定桥架数&#xff0c;不符合模型参数化需求&#xff1b; 解决方案&#xff…

java作业

以下是添加注释后的代码&#xff1a; public class DrinkSprite { public static void main(String[] args) { int money 50; // 初始的钱数 int price 6; // 每瓶雪碧的价格 int bottleReturn 1; // 退一个瓶子换回的钱数 int …

C++数据结构之:树Tree

摘要&#xff1a; it人员无论是使用哪种高级语言开发东东&#xff0c;想要更高效有层次的开发程序的话都躲不开三件套&#xff1a;数据结构&#xff0c;算法和设计模式。数据结构是相互之间存在一种或多种特定关系的数据元素的集合&#xff0c;即带“结构”的数据元素的集合&am…

Linux驱动应用编程(二)控制GPIO

本文目录 一、基础1. 命令控制方式一&#xff1a;使用 GPIO序号来控制。方式二&#xff1a;使用 GPIO组号来控制。 2. 代码控制 一、基础 我们先要通过查询开发板手册&#xff0c;来获取引脚口信息。这里我们以开发板OreangpiAIPro为例。查询官方手册如下&#xff1a; 1. 命令…

工业机器视觉系统如何实现精准检测?

机器视觉系统是指利用机器替代人眼做出各种测量和判断。一种比较复杂的系统。大多数系统监控对象都是运动物体&#xff0c;系统与运动物体的匹配和协调动作尤为重要&#xff0c;所以给系统各部分的动作时间和处理速度带来了严格的要求。在某些应用领域&#xff0c;例如机器人、…

NLP:将中文/英文文档切分多个句子

文章目录 1. 前言2. 步骤2.1安装 SpaCy2.2 下载模型2.3 加载模型并处理文本 1. 前言 SpaCy 是一个开源的自然语言处理库&#xff0c;它支持多种语言的文本处理&#xff0c;包括中文。SpaCy 对中文文本的处理主要依赖于其内置的中文分词器&#xff08;tokenizer&#xff09;。以…

Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制

多环境开发(yaml文件版) 我们在自己的开发中是自己环境 测试 生产的环境都不同 多环境分为 两个步骤 设置环境 生产环境 开发环境 测试环境 手搓三个环境 设置应用环境 应用pro配置 # 应用环境 spring:profiles:active: pro--- # 设置环境 # 生产环境 spring:profiles: p…