Kotlin的协程:挂起函数
挂起函数
挂起函数是指使用 suspend 关键字修饰的函数。
suspend fun getUserInfo(): String {withContext(Dispatchers.IO) {delay(1000L)}return "BoyCoder"
}
挂起和恢复
挂起函数与普通函数的区别在于:挂起函数可以挂起和恢复。挂起和恢复也是协程与线程相比的优势。
考虑下面一种场景:
- 获取用户信息
- 获取用户的好友
- 获取每位好友的动态
如果使用 Java,可能会这么写:
public class SuspendFunction {public void getAllFeeds() {getUserInfo(new Callback() {@Overridepublic void onSuccess(String user) {if (user != null) {System.out.println(user);getFriendList(user, new Callback() {@Overridepublic void onSuccess(String friendList) {if (friendList != null) {System.out.println(friendList);getFeedList(friendList, new Callback() {@Overridepublic void onSuccess(String feedList) {if (feedList != null) {System.out.println(feedList);}}});}}});}}});}private void getFeedList(String friendList, Callback callback) {new Thread(() -> {SystemClock.sleep(1000L);if (callback != null) {callback.onSuccess("feedList");}}).start();}private void getFriendList(String user, Callback callback) {new Thread(() -> {SystemClock.sleep(1000L);if (callback != null) {callback.onSuccess("friendList");}}).start();}private void getUserInfo(Callback callback) {new Thread(() -> {SystemClock.sleep(1000L);if (callback != null) {callback.onSuccess("user");}}).start();}public interface Callback {void onSuccess(String response);}
}
这种多重回调的模式被称为“回调地狱”,代码嵌套层次多,可读性差。
如果使用 Kotlin 的挂起函数改写,会变得很简单:
fun main() = runBlocking {val userInfo = getUserInfo()val friendList = getFriendList(userInfo)val feedList = getFeedList(friendList)println(feedList)
}suspend fun getUserInfo(): String {withContext(Dispatchers.IO) {delay(1000L)}return "BoyCoder"
}suspend fun getFriendList(user: String): String {withContext(Dispatchers.IO) {delay(1000L)}return "friendList"
}suspend fun getFeedList(friendList: String): String {withContext(Dispatchers.IO) {delay(1000L)}return "feedList"
}
挂起函数的特点是使用同步的方式完成异步任务。
以下面的代码为例
val userInfo = getUserInfo()
getUserInfo 使用 withContext 切换到 IO 线程,延迟 1 秒,然后返回结果。程序在调用 getUserInfo 时挂起,然后返回结果给 userInfo 时恢复。不需要像 Java 使用回调来传递结果。
等号 “=” 右边的代码执行在子线程并挂起,右边执行完毕后,等号 “=” 左边的代码恢复到主线程执行。
深入理解 suspend
挂起函数的本质就是 Callback。Kotlin 的编译器会将 suspend 函数转换为带有 Callback 的普通函数。
反编译之前的 Kotlin 代码,可以看出 getUserInfo 挂起函数转换为了带有 Continuation 参数的普通函数。
@Nullable
public static final Object getUserInfo(@NotNull Continuation var0) {...
}
Continuation 的定义如下:
public interface Continuation<in T> {/*** The context of the coroutine that corresponds to this continuation.*/public val context: CoroutineContext/*** Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the* return value of the last suspension point.*/public fun resumeWith(result: Result<T>)
}
Continuation 是一个接口,带有 resumeWith 方法用来返回结果。本质和 Callback 的作用是一样的。
这种将 suspend 函数转换为带有 Continuation 普通函数的过程叫做 CPS 转换。
CPS:Continuation-Passing-Style Transformation。
CPS转换就是将程序接下来要执行的代码进行传递的一种模式,它将原本的同步挂起函数转换为 Callback 异步代码。
协程之所以是非阻塞,是因为它支持“挂起和恢复”,而挂起和恢复的能力,主要来自挂起函数。挂起函数是由 CPS 实现的,其中的 Continuation,本质上是 Callback。
协程与挂起函数
协程的主要能力来自挂起函数,但是协程不等同于挂起函数。
挂起函数只能在协程或者其他挂起函数中调用。因为只有协程和其他挂起函数能够提供 Continuation。
从 runBlocking 的参数 block 可以看出,block 也是一个挂起函数。
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
因为挂起函数能够调用挂起函数,协程提供了 block 挂起函数,所以协程也能调用挂起函数。
挂起和恢复是协程的一种底层能力,而这种能力的实现,依靠的是挂起函数。
小结
- 挂起函数使用 suspend 关键字表示。
- 挂起函数能够以同步的方式写异步代码。
- 挂起函数拥有挂起和恢复的能力。
- 挂起函数的本质是 Callback,也就是 Continuation,Kotlin 编译器会完成 CPS 转换。
- 挂起函数只能在协程或者其他挂起函数中调用。
Kotlin的协程:挂起函数相关推荐
- 破解 Kotlin 协程(6) - 协程挂起篇
关键词:Kotlin 协程 协程挂起 任务挂起 suspend 非阻塞 协程的挂起最初是一个很神秘的东西,因为我们总是用线程的概念去思考,所以我们只能想到阻塞.不阻塞的挂起到底是怎么回事呢?说出来你也 ...
- Kotlin的协程与生命周期
文章目录 一.前言 二.引入依赖 三.代码示例 1.基础用法 2.repeatOnLifecycle 3.flowWithLifecycle 4.lifecycle.whenCreated.lifec ...
- 十一、kotlin的协程(一)
theme: Chinese-red 学习的前提 java线程需要大概知道点 协程是线程执行的任务, 协程和用户线程的区别在于, 协程背靠强大的编译器, 协程有专属于协程的调度器和一堆方便好用的函数, ...
- Kotlin中协程理解与实战(一)
Kotlin中协程理解与实战(一) 什么是协程 在Android中协程用来解决什么问题 协程是: suspend -也称为挂起或暂停,用于暂停执行当前协程,并保存所有局部变量: resume -用于让 ...
- Kotlin高级协程
Kotlin高级协程 一.前言 二.先从线程说起 三.协程的设计思想 四.协程特点:优雅的实现移步任务 五.协程基本使用 六.协程和线程相比有什么特点,如何优雅的实现异步任务 一.前言 在文章正式上干 ...
- 【对比Java学Kotlin】协程简史
文章目录 一.概念释义 1.1 协程定义 1.2 与线程的关系 1.3 协程简史 二.种类划分 2.1 按调用栈分类 2.2 按调度方式分类 三.异步编程 3.1 多线程 3.2 回调 3.3 Pro ...
- Kotlin(3)-协程和操作符重载,Java程序员秋招三面蚂蚁金服
Kotlin 文件和类不存在一对一关系 共生体 继承 修饰符 空指针问题 正文 重难点 协程 想了很久,关于协程的内容,在官网上确实有很多内容,基础知识概念,基本使用,以及 流操作,通道,异常处理,并 ...
- kotlin coroutines 协程教程-入门用法
kotlin coroutines 协程教程-入门用法 Coroutine 协程,是kotlin 上的一个轻量级的线程库,对比 java 的 Executor,主要有以下特点: 更轻量级的 api 实 ...
- Kotlin 之 协程
初识协程,启动取消协程,Flow异步流,协程并发 目录 (一)初识协程 协程是什么? Android中协程解决了什么问题? 协程的挂起与恢复 挂起和阻塞 协程的调度器 Dispatchers 任务泄露 ...
- 十一、kotlin的协程 - 缓存、volatile、内存屏障和cas(四) --- 跑题篇
本章写着写着就跑题了, 又不舍得删除, 新手看 # 协程的共享变量安全问题简单入门和## volatile 不保证原子性部分代码, 其他可以不看, 太乱, 也没用 协程的共享变量安全问题简单入门 在使 ...
最新文章
- OC底层原理之Runtime
- 网关Spring Cloud Gateway科普
- dba_segments和dba_tables的不同
- python 文件修改记录_python基础-文件增删改查
- Javascript第六章JavaScript用new创建对象第一课
- PyCharm2018 汉化激活
- npm安装vue-cli时速度慢,fetchMetadata经常卡住并报异常
- html 页面加载事件,页面加载事件--DOMContentLoaded
- SpringBoot 通过阿里云的短信功能 实现发送手机验证码
- css3中-moz、-ms、-webkit,-o分别代表的意思,以及微信浏览器内核分析
- 根据出生日期判断星座
- Unity3D 本地数据持久化记录存储
- java数组初始化的方式_java数组初始化方式
- 转载-工业界视频理解解决方案大汇总(人工智能前沿讲习)
- 返还delegation
- three.js 加载obj+mtl模型
- 优质计算机教案,信息技术优质课教案.docx
- cdf日上免税店_把代购删了吧!在家也可以逛免税店
- 正面硬刚华龙、副业却节节败退的姚忠良,能带白象重获新生吗?
- 【breathandlife】气势磅礴、比较好听的旋律有哪些?
热门文章
- cloc工具 命令行 统计代码行数
- 未成年人勿进 谨以献给1980~1990出生的人(一)
- c语言一维数组教学设计,C语言教学中一维数组教学设计.doc
- matlab hanning和hann,哪位大侠能告知——为什么在MATLAB2012中不能调研(汉宁)窗函...
- 一起搭建私有物联网云平台讲解
- 复旦大学python教程_复旦大学大数据学院本科生课程学习手册.PDF
- moment格式化时间
- 计算机软件企业申请商标,软件商标注册申请流程
- STM32开发笔记03---Bit-Banding
- 《Hands-On Machine Learning with Scikit-Learn TensorFlow》读书笔记(三):分类