Kotlin 协程 CoroutineScope

news2024/10/10 12:23:24

协程定义:

19年官方是这样说的:协程是轻量级的线程,协程就是 Kotlin 提供的一套线程封装的 API;

现在官方是这样说的:协程是一种并发设计模式;

协程作用:

1.处理耗时任务;

2.保证主线程的安全;

3.简化异步执行的代码,解决并发问题,让「协作式多任务」实现起来更加方便。

使用协程,同样可以像 Rx 那样有效地消除回调地狱,不过无论是设计理念,还是代码风格,两者是有很大区别的,协程在写法上和普通的顺序代码类似。

协程特点:

轻量:您可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。

内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。

内置取消支持:取消功能会自动通过正在运行的协程层次结构传播。

协程使用: 

1.lauch

lauch用于在协程作用域中异步启动一个新的协程,调用该方法不会阻塞线程。

CoroutineScope(Dispatchers.IO).launch {
	// 启动一个非阻塞线程的协程
}

2.suspend

suspend是协程的关键字,每一个被suspend修饰的方法都必须在另一个suspend函数或者Coroutine协程程序中进行调用。 

3.coroutineScope

coroutineScope是一个挂起函数,每一个被suspend修饰的方法都必须在另一个suspend函数或者Coroutine协程程序中进行调用。

CoroutineScope(Dispatchers.IO).launch {
	coroutineScope {
		// 启动一个非阻塞线程的协程
	}
}

runBlocking {
	coroutineScope {
		// 启动一个非阻塞线程的协程
	}
}

4.runBlocking

runBlocking会阻塞当前线程,而coroutineScope不会阻塞所在的线程,它会挂起所在的协程直至其内部任务(包括子协程)执行完成。

runBlocking {
	// 启动一个阻塞线程的协程
}

5.dispatcher

dispatcher 协程调度器,可以控制协程代码块在UI线程还是子线程中执行;

6.async

1)在概念上,async 就类似于 launch。它启动了一个单独的协程与其它所有的协程一起并发的工作。不同之处在于 launch 返回一个 Job 并且不附带任何结果值,而 async 返回一个 Deferred接口指向的对象,使用 Deferred.await()在一个延期的值上得到它的最终结果,同时Deferred 也是一个 Job,所以如果需要的话,可以使用Deferred.cancel()取消它。

CoroutineScope(Dispatchers.IO).launch {
	val api1Deferred = async { api1() }
	val api2Deferred = async { api2() }
	val api3Deferred = async { api3() }
	println("api*****1")
	val result1 = api1Deferred.await()
    println("api*****2")
}

suspend fun api1(): String {
	delay(2500) // 模拟耗时操作
	println("api1")
	return "api1"
}

suspend fun api2(): String {
	delay(2000) // 模拟耗时操作
	println("api2")
	return "api2"
}

suspend fun api3(): String {
	delay(1500) // 模拟耗时操作
	println("api3")
	return "api3"
}

输出结果: 

结论:

async启动的所有协程是一起并发工作的,async不是挂起函数,所以不会挂起launch创建的协程,所以先输出api*****1,再api3、api2、api1,但执行到api1Deferred.await()时,会挂起协程,等待async { api1() }执行完返回结果后,再执行协程后续的函数,所以最后输出api*****2,因为await()是挂起函数。

public suspend fun await(): T

2)案例:有4个耗时方法,方法名api1,api2,api3,api4;要求方法api1先执行,返回String "api1",然后将结果"api1"作为参数,并发执行方法api2和方法api3,由于api3 delay(1500),api2 delay(2000),所以api3会先执行完并输出结果,然后api2再执行完并输出结果,最后将结果"api2",api3"作为参数,执行方法api4。

CoroutineScope(Dispatchers.IO).launch {
	val api1Deferred = async { api1() }
	println("api*****1")
	val result1 = api1Deferred.await()
	println("api1:$result1") // 等待 api1 方法执行完成并输出结果
	val api2Deferred = async { api2(result1) }
	val api3Deferred = async { api3(result1) }
	val result2 = api2Deferred.await()
	println("api2:$result2")
	val result3 = api3Deferred.await()
	println("api3:$result3")
	println(api4(result2, result3))
}

suspend fun api1(): String {
	delay(3000) // 模拟耗时操作
	println("api1")
	return "api1"
}

suspend fun api2(v: String): String {
	delay(2000) // 模拟耗时操作
	println("api2")
	return "api2:$v"
}

suspend fun api3(v: String): String {
	delay(1500) // 模拟耗时操作
	println("api3")
	return "api3:$v"
}

suspend fun api4(v: String, v2: String): String {
	delay(500) // 模拟耗时操作
	return "api4:$v$v2"
}

输出结果:  

注:async是并行的,如果使用await()的话,await()是挂起函数,会挂起协程,等待async { api1() }执行完返回结果后,再执行协程后续的函数。

7. withContext

withContext 与 async 都可以返回耗时任务的执行结果。多个 withContext 任务是串行(顺序执行)的, 且withContext 可直接返回耗时任务的结果。 而多个 async 任务是并行的。

public suspend fun <T> withContext()

因为withContext()是挂起函数,执行后,会挂起协程,等待withContext内部函数执行完后,再执行withContext函数后面的函数。

CoroutineScope(Dispatchers.IO).launch {
	val result1 = withContext(Dispatchers.IO) {
		api1()
	}
	println("api*****1")
	println("api1:$result1") // 等待 api1 方法执行完成并输出结果
	val result2 = withContext(Dispatchers.IO) {
		api2(result1)
	}
	println("api2:$result2")
	val result3 = withContext(Dispatchers.IO) {
		api3(result1)
	}
	println("api3:$result3")
	val result4 = withContext(Dispatchers.IO) {
		api4(result2, result3)
	}
	println(result4)
}

suspend fun api1(): String {
	delay(3000) // 模拟耗时操作
	println("api1")
	return "api1"
}

suspend fun api2(v: String): String {
	delay(2000) // 模拟耗时操作
	println("api2")
	return "api2:$v"
}

suspend fun api3(v: String): String {
	delay(1500) // 模拟耗时操作
	println("api3")
	return "api3:$v"
}

suspend fun api4(v: String, v2: String): String {
	delay(500) // 模拟耗时操作
	return "api4:$v$v2"
}

输出结果:   

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

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

相关文章

【雕爷学编程】Arduino动手做(172)---WeMos D1开发板模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

RL 实践(4)—— 二维滚球环境【DQN Double DQN Dueling DQN】

本文介绍如何用 DQN 及它的两个改进 Double DQN & Dueling DQN 解二维滚球问题&#xff0c;这个环境可以看做 gym Maze2d 的简单版本参考&#xff1a;《动手学强化学习》完整代码下载&#xff1a;5_[Gym Custom] RollingBall (DQN and Double DQN and Dueling DQN) 文章目录…

智能喷涂机器人的制作分享

作者&#xff1a;朱家谊、吾丽江、管孝天 单位&#xff1a;天津工业大学 指导老师&#xff1a;李鹏 1. 概念说明 智能喷涂机器人是一种具有自主感知、决策和执行能力的机器人&#xff0c;专门用于自动化喷涂任务&#xff0c;它可以应用于各种领域&#xff0c;如汽车制造、建…

【已解决】jupyter notebook里已经安装了第三方库,还是提示导入失败

在jupyter notebook中运行Python代码&#xff0c;明明已经安装了第三方库&#xff0c;还是提示导入失败。 以导入pandas库为例&#xff0c;其他库同理&#xff1a; 报错代码&#xff1a; import pandas报错原因&#xff1a; 电脑上存在多个python运行环境&#xff08;比如&a…

JavaScript学习 -- Hex编码

Hex编码是一种十六进制数字的表示方式。在JavaScript中&#xff0c;我们可以使用Hex编码来表示数字、颜色和其他二进制数据&#xff0c;并将其用于各种场景&#xff0c;例如Web开发、图像处理和加密解密等。在本篇博客中&#xff0c;我们将介绍Hex编码的基础知识和相关技术&…

Xilinx FPGA平台GTX简易使用教程(汇总篇)

GTX简易使用教程&#xff0c;先“知其然”&#xff0c;慢慢再研究“所以然”。 目录 一、GTX必备基础知识 二、时钟篇 三、复位与初始化 四、GTX IP核配置介绍 五、GTX收发测试 六、后记 一、GTX必备基础知识 虽说搬砖只需要会用IP就行&#xff0c;但是为了把砖搬好&a…

js:浏览器环境下复制图片到剪切板

浏览器环境下复制图片到剪切板思路&#xff1a; 通过canvas将图片url转为base64格式将base64格式转为Blob类型的数据调用浏览器接口复制内容到剪切板 图片处理工具方法 image-util.js // Image对象转base64 export function imageToBase64(image) {let canvas document.cr…

Canal深入调研

Canal深入调研 1.canal的设计 1.1 Canal的设计理念 canal的组件化设计非常好&#xff0c;有点类似于tomcat的设计。使用组合设计&#xff0c;依赖倒置&#xff0c;面向接口的设计。 说明&#xff1a; ​ server代表一个canal运行实例&#xff0c;对应于一个jvm ​ instance…

免费分享一套基于SpringBoot实现商城系统(仿天猫),挺漂亮的

大家好&#xff0c;我是锋哥&#xff0c;看到一个不错的基于SpringBoot实现商城系统(仿天猫)系统&#xff0c;分享下哈。 项目介绍 迷你天猫商城是一个基于Spring Boot的综合性B2C电商平台&#xff0c;需求设计主要参考天猫商城的购物流程&#xff1a;用户从注册开始&#xf…

编码器原理

编码器原理 编码器是一种用来测量机械旋转或位移的传感器。这种传感器能够测量机械部件在旋转或直线运动时的位移位置或速度等信息,并将其转换成一系列电信号。 光栅式旋转编码器 霍尔式编码器

制作一个简易的计算器app

1、Ui开发 笔者的Ui制作的制作的比较麻烦仅供参考&#xff0c;在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单 笔者大致划分是这样的&#xff1a; 使用了四个大框&#xff0c;在第四个大框里面有多个小框 最终界面如下&am…

Linux(一)

一.FinalShell远程连接Linux系统&#xff08;可能在自己电脑虚拟机上也可能在服务器上&#xff09; 二.掌握使用WSL获得Ubuntu系统环境 WSL作为Windows10系统带来的全新特性。 传统方式获取Linux操作系统环境&#xff0c;是安装完整的虚拟机&#xff0c;如VMware 使用WSL&#…

系统架构设计师 10:软件架构的演化和维护

一、软件架构演化 如果软件架构的定义是 SA{components, connectors, constraints}&#xff0c;也就是说&#xff0c;软件架构包括组件、连接件和约束三大要素&#xff0c;这类软件架构演化主要关注的就是组件、连接件和约束的添加、修改与删除等。 二、面向对象软件架构演化…

点云分割-pcl区域生长算法

目录 写在前面原理代码运行结果 参考完 写在前面 1、本文内容 pcl的区域生长算法的使用和原理 2、平台/环境 cmake, pcl 3、转载请注明出处&#xff1a; https://blog.csdn.net/qq_41102371/article/details/131927376 原理 参考&#xff1a;https://pcl.readthedocs.io/pr…

从风控系统看架构设计原型图分析

目录 一、对架构与架构图的理解 &#xff08;一&#xff09;架构的本质 &#xff08;二&#xff09;软件设计中架构域的划分 &#xff08;三&#xff09;架构图设计 架构图设计的必要性 如何画架构图 二、实践业务架构与产品架构设计 &#xff08;一&#xff09;列出问…

基于SpringBoot+vue的学生成绩管理系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

重学C++系列之虚继承

一、什么是虚继承 虚继承是C中一种继承的机制。继承有普通继承和虚继承两种机制&#xff0c;默认是普通继承&#xff0c;如果要使用多继承&#xff0c;需要在继承方式里加上关键字virtual。 二、为什么要虚继承 在C中&#xff0c;为解决二义性问题&#xff0c;引入虚继承机制。…

tinkerCAD案例:9. Saw Shaped Wrench 锯形扳手

tinkerCAD案例&#xff1a;9. Saw Shaped Wrench 锯形扳手 ln this lesson you will learn how to create a cool saw shaped wrench. 在本课中&#xff0c;您将学习如何制作一个很酷的锯形扳手。 Start the lesson by dragging a polygon to the workplane. 通过将多边形拖动…

从模型坐标到屏幕坐标

在 3D 引擎中&#xff0c;场景通常被描述为三维空间中的模型或对象&#xff0c;每个模型对象由许多三维顶点组成。最终&#xff0c;这些模型对象将在平面屏幕上呈现和显示。 渲染场景始终相对于摄像机&#xff0c;因此&#xff0c;还必须相对于摄像机的视图定义场景的顶点。了…

zeppelin spark kerberos 使用过程遇到的问题

参考配置教程: Configure Zeppelin for a Kerberos-Enabled Cluster zeppelin spark kerberos 使用过程遇到的问题 ambari创建zeppelin时,会创建Kerberos account and keytab /etc/security/keytabs/zeppelin.server.kerberos.keytab interpreter配置 keytab Interpreter Ke…