协程&挂起&恢复

协程

协程通用概念:Coroutination passing style(CPS),即传接续体风格。

kotlin协程可以简化为【状态机+续体CPS】

  • 续体负责传递上一步骤的执行结果和后续操作步骤
  • 状态机负责记录和转移执行状态(这里值执行点位置)

挂起

一段协程的代码,例如通过launch启动一个协程,内部的代码,如果存在挂起点,那么就会以挂起点为分界把代码包装为一个续体类,也就是协程CPS转换。

在kotlin中,协程CPS本质也确实是做了一个callback的处理(在编译期)。

在上面的【状态机+续体CPS】 模型中,挂起点之前的代码,就是状态机状态转移前执行,随后把挂起点后的代码包装为续体coroutination等待恢复后继续执行。 这里在包装续体时,会将当前续体以及状态一起传递给新的续体(或称子续体)

恢复

恢复机制:挂起点通知续体恢复。例如我们常用的suspendCoroutine{}, 在挂起点通过调用续体的resume/resumeWith来通知恢复。

kotlin 复制代码
suspend fun doSomething(): String = suspendCoroutine {
    net.getDataFromRemote() { data ->
        it.resume(data)
    }
}

Q:遇到挂起点了,不是已经被挂起了吗....为什么挂起点还能通知恢复?是什么机制 ?

A:

  • 挂起点也是另一个执行点,如果把协程简化为线程池中的runnable,那么挂起点内部也是一个runnable,只不过给它传递了父协程,然后在内部runnable执行结束时,调用父runnable的resumeWith
  • 这也是为什么协程切换可能会发生在同一个线程上的原因
  • 能恢复父协程继续往下执行的原因也在于,在挂起之前执行状态会转移到下一个状态,因此在恢复时就可以根据这个状态找到对应的恢复点继续执行

和线程对比

协程也常常被称为轻量级线程。

线程:

  • 操作系统底层支持的,CPU执行的最小单元
  • 线程间内存空间是独立的,线程切换需要较大的系统开销
    • 上下文保存和恢复,涉及了用户态和内核态的切换
  • 启动一个线程执行任务,那么线程就会被这个任务阻塞;
  • 由于资源占用和系统开销,线程开启的数量往往是有限的、较少的

协程:

  • 协程是上层设计,不同的开发语言有不同的实现
  • 不同的协程是共享线程资源的,协程间的切换就不涉及太多的系统开销,也不涉及用户态和内核态的切换
  • 协程是非阻塞式挂起,即不阻塞线程的,因此即使在安卓开发中也可以放心使用
  • 协程可以同时启动很多,甚至是数以万计

你可参考:

相关推荐
曹牧8 分钟前
JSON 数组的正确使用方式
java·服务器·前端
小村儿36 分钟前
一起吃透 Claude Code,告别 AI 编程迷茫
前端·后端·ai编程
小金鱼Y1 小时前
🔥 前端人必看:浏览器安全核心知识点全解析(XSS/CSRF/DDoS)
前端·javascript·安全
时寒的笔记1 小时前
js逆向05_ob混淆花指令,平坦流,某麦网(突破ob混淆寻找拦截器)
开发语言·前端·javascript
ZengLiangYi1 小时前
从文章到脚本:把 Git Tag + Semver + CI/CD 收敛成一个 `release.mjs`
前端·github
im_AMBER1 小时前
Lexical依赖版本冲突与标题渲染
前端·react.js·前端框架
起风了___1 小时前
解决大数据渲染卡顿:Vue3 虚拟列表组件的完整实现方案
前端·程序员
前端fun1 小时前
React如何远程加载组件
前端·react.js
代码煮茶1 小时前
Vue3 路由实战 | Vue Router 从 0 到 1 搭建权限管理系统
前端·javascript·vue.js
gaozhiyong08132 小时前
深度技术拆解:豆包2 Pro vs Gemini 3—国产工程派与海外原生派的巅峰对决
前端·spring boot·mysql