协程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文件用于存…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...