协程1 --- 发展历史
文章目录
- 一个编译器问题
- 背景
- 解决
- 协程为什么一开始没发展成一等公民?
- 自顶向下、逐步求精(Top-down, stepwise refinement)
- 线程的出现
- 协程的雄起
- IO密集型
- 同步语义实现异步
- 发展史
- 线程和协程的关系
- 并发性
- 调度方式
- 资源占用
一个编译器问题
协程概念的出现比线程更早,可以追溯到20世纪50年代的一个问题:COBOL编译器无法做到读一次磁带就可以完成整个编译过程
背景
编译器的基本步骤包括:读取字符流、词法分析、语法分析、语义分析、代码生成器、代码优化器等,上一步的输出作为下一步的输入。
COBOL程序被写在一个磁带上,而磁带不支持随机读写(fopen指针不能fseek)。- 计算机内存小又不可能把整个磁带的内容都装进去,所以一次读取没编译完就要再从头读。
解决
将词法分析和语法分析合作运行,而不再像其他编译器那样相互独立,两个模块交织运行,编译器的控制流在词法分析和语法分析之间来回切换:
- 当词法分析模块基于词素产生足够多的词法单元Token时就控制流转给语法分析
- 当语法分析模块处理完所有的词法单元Token时将控制流转给词法分析模块
- 词法分析和语法分析各自维护自身的运行状态,并且具备主动让出和恢复的能力

协程为什么一开始没发展成一等公民?
自顶向下、逐步求精(Top-down, stepwise refinement)
自顶向下的思想:对要完成的任务进行分解,先对最高层次中的问题进行定义、设计、编程和测试,而将其中未解决的问题作为一个子任务放到下一层次中去解决。这样逐层、逐个地进行定义、设计、编程和测试,直到所有层次上的问题均由实用程序来解决,就能设计出具有层次结构的程序。
然而协程这种相互协作调度的思想和自顶向下是不合的,在协程中各个模块之间存在很大的耦合关系,不符合高内聚低耦合的编程思想,相比之下自顶向下的设计思想使程序结构清晰、层次调度明确,代码可读性和维护性都很不错。
线程的出现
抢占式的线程可以解决大部分的问题,也就是说协程能干的线程干得也不错,线程干的不好的地方,使用者暂时也可以接受。(抢占式任务系统依赖于CPU硬件的支持,对硬件要求比较高,对于一些嵌入式设备来说,协同调度再合适不过了,所以协程在另外一个领域也施展了拳脚。)
协程的雄起
技术、思想等发展很大程度受时代和场景的影响
IO密集型
对于CPU来说,任务分为两大类:计算密集型和IO密集型。
IO密集型提高CPU有效利用率一直是个难点。
在抢占式调度中也有对应的解决方案:异步+回调
整个过程相比同步IO来说,原来整体的逻辑被拆分为好几个部分,各个子部分有状态的迁移,逻辑复杂,bug肯定多。
同步语义实现异步
前端经典的回调地狱:

例子:
<script>function doSomething1() {return 'doSomething1'}function doSomething2() {return 'doSomething2'}function doSomething3() {return 'doSomething3'}setTimeout(() => {console.log(doSomething1())setTimeout(() => {console.log(doSomething2())setTimeout(() => {console.log(doSomething3())}, 1000)}, 1000)}, 1000)
</script>
用同步的写法实现异步效果:
<script>function doSomething1() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('doSomething1')}, 1000)})}function doSomething2() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('doSomething2')}, 1000)})}function doSomething3() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('doSomething3')}, 1000)})}(async () => {console.log(await doSomething1())console.log(await doSomething2())console.log(await doSomething3())})();
</script>
发展史

- 1966 年,线程(thread)的概念被提出。
- 1968 年,Dijkstra 发表论文《GOTO 语句是有害的》,结构化编程的理念深入人心,自顶向下的程序设计思想成为主流,协程“跳来跳去”的执行行为类似 goto 语句,违背自顶向下的设计思想。
- 1979 年,Marlin 提交博士论文 Coroutines : A Programming Methodology, A Language Design, and An Implementation,是协程理论的集大成之作。
- 1980 年及之后的 20 余年,多线程成为并发编程的代名词,抢占式击败协作式成为主流的调度方式,协程逐渐淡出主流编程语言舞台。
- 2003 年,Lua v5.0 版本开始支持协程。
- 2005 年,Python 开始支持生成器和 yield/send 关键字,之后数年一直在演化。
- 2009 年,Go 语言问世,以 Goroutine 的方式支持并发编程,一代传奇拉开序幕。
- 2012 年,C# 开始支持 async 函数和 await 表达式,标志着协程王者归来。
- 2015 年,Python 支持 async/await 语法。
- 2017 年,async/await 纳入 ES2017 标准。
- 2017 年,Kotlin 另辟蹊径,以 suspend 关键字的形式实现了协程。
- 2019 年,Dart 支持 Future、async/await 语法。
- 2020 年,C++ 20 支持 co_async/co_await。
- 2022 年 3 月,JDK 19 预览版(Early-Access)中引入了一种新的并发编程模型(织布机计划)——虚拟线程,非最终版,可能随时被删除。
线程和协程的关系
协程和线程并非矛盾,协程的威力在于IO的处理,恰好这部分是线程的软肋,由对立转换为
合作才能开辟新局面。
从进程到线程再到协程,我们对于CPU的压榨从未停止。
linux2.6之后的线程切换耗时大概是在几微秒,协程大概是在100纳秒左右。
并发性
线程在多核的环境下是能做到真正意义上的并行执行的,而协程是为并发而生的。
打个简单的比方,射雕英雄传中周伯通教郭靖一手画圆,一手画方,两只手同时操作,这个就是并行。普通人大概率不能并行,却可以并发,你先左手画一笔,然后右手画一笔,同一时候只有一只手在操作,来回交替,直到完成两个图案,这就是并发,协程主要的功能。

调度方式
- 线程 – 抢占式调度,操作系统内核调度,切换涉及到
内核态和用户态的转换,开销较大。 - 协程 – 协作式调度,由程序自身控制,切换只需要保存和恢复协程的上下文,开销较小。
协程的控制可能造成某些协程的饥饿,抢占式更加公平
协程的控制权由用户态决定可能转移给某些恶意的代码,抢占式由操作系统来调度更加安全
资源占用
- 线程 – 有自己独立的栈空间(linux默认8MB),TCB等
- 协程 – 栈空间可以根据需要
动态调整,共享线程的内核资源,一般设置的128K
相关文章:
协程1 --- 发展历史
文章目录 一个编译器问题背景解决 协程为什么一开始没发展成一等公民?自顶向下、逐步求精(Top-down, stepwise refinement)线程的出现 协程的雄起IO密集型同步语义实现异步发展史 线程和协程的关系并发性调度方式资源占用 一个编译器问题 协…...
VBA10-处理Excel的动态数据区域
end获取数据边界 1、基本语法 1-1、示例: 2、配合row和column使用 2-1、示例1 2-2、示例2 此时,不管这个有数值的区域,怎么增加边界,对应的统计数据也会跟着变的!...
【git】使用记录
一、安装 参考:Git2.45.2下载安装记录(windows 11)_win11安装git-CSDN博客...
代码随想录算法训练营第三十八天|Day38 动态规划
322. 零钱兑换 视频讲解:https://www.bilibili.com/video/BV14K411R7yv https://programmercarl.com/0322.%E9%9B%B6%E9%92%B1%E5%85%91%E6%8D%A2.html 思路 #define min(a, b) ((a) > (b) ? (b) : (a)) int coinChange(int* coins, int coinsSize, int amount…...
使用C++和libcurl库实现HTTP请求(GET、POST、文件上传)
在现代软件开发中,与外部API服务进行通信已成为常见需求。本文将展示如何使用C和libcurl库实现基本的HTTP请求,包括GET请求、POST请求(带JSON数据)以及包含文件上传的POST请求。 准备工作 首先,需要确保已安装libcur…...
makefile例子
$指代当前目标,就是Make命令当前构建的那个目标。比如,make foo的 $ 就指代foo。 $< 指代第一个前置条件。比如,规则为 t: p1 p2,那么$< 就指代p1。 $? 指代比目标更新的所有前置条件,之间以空格分隔。比如&a…...
用环形数组实现队列(多种高级方法,由浅入深)
同普通数组实现的队列相比,普通数组的头结点和尾节点都是固定的,在进行移除的时候如果移除了一个节点,后面所有节点都需要进行移除操作,需要的时间复杂度更高 在环形数组中,确定了头尾指针的环形数组很好地解决了这一…...
springboot框架使用RabbitMQ举例代码
以前分享过一个理论有兴趣的小伙伴可以看下 https://blog.csdn.net/Drug_/article/details/138164180 不多说 还是直接上代码 第一步:引入依赖 可以不指定版本 <!-- amqp --><dependency><groupId>org.springframework.boot</groupId…...
Java实现一个延时队列
文章目录 前言正文一、基本概念1.1 延时队列的特点1.2 常见的实现方式 二、Java原生的内存型延时队列2.1 定义延时元素DelayedElement2.2 定义延时队列管理器DelayedQueueManager2.3 消费元素2.4 调试2.5 调试结果2.6 精髓之 DelayQueue.poll() 三、基于Redisson的延时队列3.1 …...
为什么说vue是双向数据流
Vue.js 被称为 双向数据绑定(two-way data binding),是因为它支持数据在 视图(View) 和 模型(Model) 之间双向流动。这意味着,当 数据变化 时,视图会自动更新;…...
创造属于你的 Claude Prompt 和个性化 SVG 卡片|对李继刚老师提示词的浅浅解析与总结
❤️ 如果你也关注大模型与 AI 的发展现状,且对大模型应用开发非常感兴趣,我会快速跟你分享最新的感兴趣的 AI 应用和热点信息,也会不定期分享自己的想法和开源实例,欢迎关注我哦! 🥦 微信公众号ÿ…...
redis与本地缓存
本地缓存是将数据存储在应用程序所在的本地内存中的缓存方式。既然,已经有了 Redis 可以实现分布式缓存了,为什么还需要本地缓存呢?接下来,我们一起来看。 为什么需要本地缓存? 尽管已经有 Redis 缓存了,但…...
git撤销commit和add
撤销commit git reset --soft HEAD^撤销add git reset .查看状态 git status...
【361】基于springboot的招生宣传管理系统
摘 要 使用旧方法对招生宣传管理系统的信息进行系统化管理已经不再让人们信赖了,把现在的网络信息技术运用在招生宣传管理系统的管理上面可以解决许多信息管理上面的难题,比如处理数据时间很长,数据存在错误不能及时纠正等问题。这次开发的招…...
【一些关于Python的信息和帮助】
Python是一种广泛使用的高级编程语言,它的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而不是使用大括号或关键字)。Python支持多种编程范式,包括面向对象、命令式、函数式和过程式编程。 以…...
creo toolkit二次开发学习之程序集(ProAsmcomp)和装配体组件路径对象(ProAsmcomppath)
程序集ProAsmcomp可以理解为装配体组件对象。 对象ProAssembly是ProSolid的一个实例,并共享相同的声明。因此,ProAssembly对象可以作为适用于装配体的任何ProSolid和ProMdl函数的输入。特别是,因为你可以使用函数ProSolidFeatVisit()来遍历特…...
深入浅出 Spring Boot 与 Shiro:构建安全认证与权限管理框架
一、Shiro框架概念 (一)Shiro框架概念 1.概念: Shiro是apache旗下一个开源安全框架,它对软件系统中的安全认证相关功能进行了封装,实现了用户身份认证,权限授权、加密、会话管理等功能,组成一…...
外包干了三年,精神严重内耗...
前段时间我同事(做测试的一个妹子)跟我讲,感觉早上起来十分的疲惫,不想上班,问我们这是什么样的现象,其实有时候我也有这种感觉,虽然我卷,但我也是肉体凡胎啊!不是机器人…...
ruoyi-vue集成tianai-captcha验证码
后端代码 官方使用demo文档:http://doc.captcha.tianai.cloud/#%E4%BD%BF%E7%94%A8demo 我的完整代码:https://gitee.com/Min-Duck/RuoYi-Vue.git 主pom.xml 加入依赖 <!-- 滑块验证码 --><dependency><groupId>cloud.tianai.captc…...
Django安装
在终端创建django项目 1.查看自己的python版本 输入对应自己本机python的版本,列如我的是3.11.8 先再全局安装django依赖包 2.在控制窗口输入安装命令: pip3.11 install django 看到Successflully 说明我们就安装成功了 python的Scripts文件用于存…...
C++ STL 容器内存管理机制
C STL容器内存管理探秘 在C开发中,STL(标准模板库)容器是高效数据处理的基石,其背后的内存管理机制直接影响程序性能与资源利用率。理解容器如何动态分配、释放内存,不仅能避免内存泄漏和碎片化问题,还能优…...
LangFlow实战案例:如何用拖拽方式搭建智能写作助手
LangFlow实战案例:如何用拖拽方式搭建智能写作助手 1. 引言:为什么选择LangFlow 在AI技术快速发展的今天,大语言模型已经展现出强大的文本生成能力。然而,对于大多数非技术背景的内容创作者来说,直接调用API或编写复…...
面向生产的Chatgpt5.4:系统集成、架构模式与成本优化深度拆解
对于计划将顶级AI能力深度集成至自身产品与工作流的团队而言,理解Gemini 3.1 Pro的系统级特性、集成模式与全生命周期成本至关重要。国内开发者可通过RskAi(www.rsk.cn)等聚合平台,以零成本、国内直访的方式完成前期技术验证与原型…...
浅谈:区块链存在的三点隐患问题
上文我们讲了区块链这个话题,有读者可能会有疑问:如果说区块链技术如此完美,为什么我们现在还没有广泛地用上它呢?实际上,区块链技术还处于发展早期,还在讨论和推进当中,而区块链的技术发展也较…...
pnpm报错Node版本不兼容?3分钟学会用nvm-windows切换Node版本(含LTS版本选择建议)
pnpm报错Node版本不兼容?3分钟学会用nvm-windows切换Node版本(含LTS版本选择建议) 刚接手新项目时,我习惯性输入pnpm install准备安装依赖,却看到刺眼的报错提示:"ERROR: This version of pnpm requi…...
FHE实战:用Python体验全同态加密的医疗数据分析案例
FHE实战:用Python体验全同态加密的医疗数据分析案例 医疗数据隐私保护一直是行业痛点。某三甲医院曾因数据共享导致50万患者信息泄露,直接损失超2亿元。而全同态加密(Fully Homomorphic Encryption, FHE)技术允许在加密数据上直接…...
工业相机丢帧问题全解析:从硬件到软件的5个实战解决方案
工业相机丢帧问题全解析:从硬件到软件的5个实战解决方案 在机器视觉系统的实际应用中,工业相机丢帧问题就像一条潜伏的生产线杀手——它可能悄无声息地导致检测漏判、定位偏差甚至整批产品质检失效。去年某汽车零部件厂商就曾因2%的随机丢帧,…...
Qwen3-32B-Chat模型微调指南:提升OpenClaw任务执行准确率
Qwen3-32B-Chat模型微调指南:提升OpenClaw任务执行准确率 1. 为什么需要微调Qwen3-32B-Chat模型? 在使用OpenClaw进行自动化任务时,我发现某些特定场景下的任务执行准确率始终不理想。比如截图识别文字时,模型经常混淆相似字符&…...
如何在树莓派上搭建Spotify Connect客户端:Raspotify完整安装配置指南
如何在树莓派上搭建Spotify Connect客户端:Raspotify完整安装配置指南 【免费下载链接】raspotify A Spotify Connect client that mostly Just Works™ 项目地址: https://gitcode.com/gh_mirrors/ra/raspotify 想要将你的树莓派变成专业的Spotify Connect音…...
http-server终极指南:3分钟学会零配置静态HTTP服务器部署
http-server终极指南:3分钟学会零配置静态HTTP服务器部署 【免费下载链接】http-server a simple zero-configuration command-line http server 项目地址: https://gitcode.com/gh_mirrors/ht/http-server http-server是一款简单高效的零配置命令行静态HTTP…...
