协程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文件用于存…...

Ubuntu 20.04 安装 QGC v4.3 开发环境
Ubuntu 20.04 安装 QGC开发环境 1. 准备安装 Qt 5.15.2安装依赖获取源码 2. 编译参考 前言 QGC ( QGroundControl) 是一个开源地面站,基于QT开发的,有跨平台的功能。可以在Windows,Android,MacOS或Linux上运行。它可以将PX4固件加…...

WPF+MVVM案例实战(二十一)- 制作一个侧边弹窗栏(AB类)
文章目录 1、案例效果1、侧边栏分类2、AB类侧边弹窗实现1.文件创建2、样式代码与功能代码实现3、功能代码实现 3 运行效果4、源代码获取 1、案例效果 1、侧边栏分类 A类 :左侧弹出侧边栏B类 :右侧弹出侧边栏C类 :顶部弹出侧边栏D类 …...

linux中怎样登录mysql数据库
在Linux中登录MySQL数据库,可以使用以下命令: mysql -u username -p 其中,username是你的MySQL用户名。运行该命令后,系统会提示你输入密码。 如果MySQL服务器不在本地主机或者你需要指定不同的端口,可以使用以下命…...

深入理解 Linux 内存管理:free 命令详解
在 Linux 系统中,内存是关键的资源之一,管理和监控内存的使用情况对系统的稳定性和性能至关重要。free 命令是 Linux 中用于查看内存使用情况的重要工具,它可以让我们快速了解系统中物理内存和交换分区(Swap)的使用状态…...

指针万字超级最强i解析与总结!!!!!
文章目录 1.内存和地址1.1内存1.2究竟该如何理解编址 2.指针变量和地址2.1 取地址操作符(&)2.2指针变量和解引用操作符(*)2.2.1指针变量2.2.2如何拆解指针类型2.2.3解引用操作符 2.3 指针变量的大小 3.指针变量类型的意义3.1指…...

告别生硬电子音,这款TTS软件让语音转换更自然动听
Balabolka是一款革新性的文本语音转换工具,为用户提供了极其灵活和个性化的阅读体验。这款软件不仅仅是简单的文字朗读器,更是一个智能的语音助手,能够将各类文本瞬间转化为生动自然的语音输出。 软件的核心优势在于其卓越的文件兼容性和多样…...

CORS(跨域资源共享)和SOP(同源策略)
CORS(跨域资源共享)和SOP(同源策略)不是同一个东西,但它们紧密相关,并且常常一起讨论,因为 CORS 是为了解决同源策略带来的跨域问题而引入的。 同源策略(Same-Origin Policy&#x…...

【系统设计】数据库压缩技术详解:从基础到实践(附Redis内存优化实战案例)
概述 在现代数据库系统中,压缩技术对于提高存储效率和加速查询性能至关重要。特别是在处理大规模数据时,压缩能够极大地减少存储空间,并优化查询性能。本文将总结几种常见的压缩方式,并通过详细的解释和示例清晰地展示每种压缩方…...

基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现(源码+数据库+文档+PPT)
基于SpringBoot的“乐校园二手书交易管理系统”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统首页界面图 用户注册界面图 二手…...

debian11安装最新rabbitmq
1、使用官网提供系统对应的安装脚本 安装 版本说明: Debian Buster代表Debian 10 Debian Bullseye代表Debian 11 Debian Bookworm代表Debian 12 Debian Trixie代表Debian 13 Debian Sid代表Debian unstable版本 2、新建脚本文件 vim rabbitMq.sh将脚本内容复制到…...