什么网站动物和人做的吗,上海网站建设特点,网站售后维护,怎样做国际网站平台文章目录 一个编译器问题背景解决 协程为什么一开始没发展成一等公民#xff1f;自顶向下、逐步求精#xff08;Top-down, stepwise refinement#xff09;线程的出现 协程的雄起IO密集型同步语义实现异步发展史 线程和协程的关系并发性调度方式资源占用 一个编译器问题
协… 文章目录 一个编译器问题背景解决 协程为什么一开始没发展成一等公民自顶向下、逐步求精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肯定多。
同步语义实现异步
前端经典的回调地狱 例子
scriptfunction 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用同步的写法实现异步效果
scriptfunction 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