保存的文章

来源:互联网 发布:audition for mac 编辑:程序博客网 时间:2024/05/25 21:34
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:阿猫
链接:https://www.zhihu.com/question/20511233/answer/24260355
来源:知乎

  • 一开始大家想要同一时间执行那么三五个程序,大家能一块跑一跑。特别是UI什么的,别一上计算量比较大的玩意就跟死机一样。于是就有了并发,从程序员的角度可以看成是多个独立的逻辑流。内部可以是多cpu并行,也可以是单cpu时间分片,能快速的切换逻辑流,看起来像是大家一块跑的就行。
  • 但是一块跑就有问题了。我计算到一半,刚把多次方程解到最后一步,你突然插进来,我的中间状态咋办,我用来储存的内存被你覆盖了咋办?所以跑在一个cpu里面的并发都需要处理上下文切换的问题。进程就是这样抽象出来个一个概念,搭配虚拟内存、进程表之类的东西,用来管理独立的程序运行、切换。
  • 后来一电脑上有了好几个cpu,好咧,大家都别闲着,一人跑一进程。就是所谓的并行
  • 因为程序的使用涉及大量的计算机资源配置,把这活随意的交给用户程序,非常容易让整个系统分分钟被搞跪,资源分配也很难做到相对的公平。所以核心的操作需要陷入内核(kernel),切换到操作系统,让老大帮你来做。
  • 有的时候碰着I/O访问,阻塞了后面所有的计算。空着也是空着,老大就直接把CPU切换到其他进程,让人家先用着。当然除了I\O阻塞,还有时钟阻塞等等。一开始大家都这样弄,后来发现不成,太慢了。为啥呀,一切换进程得反复进入内核,置换掉一大堆状态。进程数一高,大部分系统资源就被进程切换给吃掉了。后来搞出线程的概念,大致意思就是,这个地方阻塞了,但我还有其他地方的逻辑流可以计算,这些逻辑流是共享一个地址空间的,不用特别麻烦的切换页表、刷新TLB,只要把寄存器刷新一遍就行,能比切换进程开销少点。
  • 如果连时钟阻塞、 线程切换这些功能我们都不需要了,自己在进程里面写一个逻辑流调度的东西。那么我们即可以利用到并发优势,又可以避免反复系统调用,还有进程切换造成的开销,分分钟给你上几千个逻辑流不费力。这就是用户态线程
  • 从上面可以看到,实现一个用户态线程有两个必须要处理的问题:一是碰着阻塞式I\O会导致整个进程被挂起;二是由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力。如果一种实现使得每个线程需要自己通过调用某个方法,主动交出控制权。那么我们就称这种用户态线程是协作式的,即是协程
本质上协程就是用户空间下的线程
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:饭后温柔
链接:https://www.zhihu.com/question/20511233/answer/24459911
来源:知乎

我觉得线程是很丑陋的东西。线程不过是反映了当前硬件技术的物理限制瓶颈。单个cpu的计算能力不足,所以要多核。内存的容量太小太昂贵,所以需要硬盘。无须敬畏,当你认识到线程不过是个妥协的产物,学习的难度就低多了。比如计算能力低引入了多核,多核引入了并发,并发引入了竞态,竞态引入了锁,一层又一层的引入了复杂性,我等程序员的饭碗才能保住。当然有些问题确实不是单纯的计算能力或存储能力极大提升就能解决的,不是我的工作范围,就不献丑了。

协程比线程更基础。协程不能像线程那样,简单看做一种硬件妥协机制。协程是可以作为语言的内建机制的,因为协程反映了程序逻辑的一种需求:可重入能力。这个能力很重要,因为大多数语言的一个最重要的组件--函数,其实就依赖这个能力的弱化版本。函数中的局部变量,被你初始化为特定的值,每次你调函数,换种说法:重入函数,语言都保证这些局部变量的值不会改变。相同的输入,得到相同的输出。当然你跟我扯全局变量就没意思了。

语言实现到函数这一步,可以满足绝大多数日常需求了。但工程师就是又懒又爱折腾啊。函数在很多语言特别是早期语言中,有个别名:过程(具体特性不一定相同,就不追究了,整体的行为还是差不离的)。我觉得过程这个词比函数更贴切。现在我们把“函数中局部变量的值”换种说法,叫做“过程中的局部状态”,这个说法更广泛了。每次重入过程,过程中的局部状态都被重置。要想得到不同的输出状态,唯有改变输入的状态。要想明确一个输出状态,对应的输入状态,唯有记录下输入状态。so simple,so native。问题是那帮懒惰的工程师甚至连输入状态都不想保存判断啊。他们希望有这么一种过程,每次进入,过程里的局部状态,都能保留上一次进入的状态。自然也就不需要干针对输入状态的保存或判断工作了。换言之,这个特殊过程把原来需要在过程之外的用来控制过程输出状态的那些输入状态的管理工作,都交给过程本身了。

这个特殊的过程,就叫做协程。它能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。普通过程(函数)可看成这个特殊过程的一个特例:只有一个状态,每次进入时局部状态重置。这种逻辑控制上的方便当然让这帮懒惰的工程师乐翻了天,少打了好多字,可以向老板叫嚣生产力提高了,其实又可以多lol几把了,对不对?用协程的好处是,你处在更高的逻辑层面去审视需求,整理代码。没有函数之前,你只有表达式,没有表达式之前,你只有mov。没有if-else之前,你只有jump。脱离低级的逻辑处理,就意味着能在更高的抽象层面看问题。就好像如果你在算傅里叶变换时,还要每次去思考四则混合运算规则,只能是自作死。协程之所以陌生,是因为这个能力很强大,因此通常跟实际业务联系很紧密吧。

因此,协程不过是一个逻辑控制需求。一些语言原生支持,不支持也可以用原有的材料构建出来。协程的实现,无非是你要维护一组局部状态,在重新进入协程前,保证这些状态不被改变,你能顺利定位到之前的位置。你平时所写的一些逻辑控制代码,经典如状态机或对象等,也许就已经是一种“协程”了。区别在于是否精巧,适用条件是否苛刻,使用是否方便,效率是否足够罢了。

面向对象中的对象,函数式语言中过程的chunk实现,都跟协程有些相似的结构。这些语言的表达足够丰富,有没有协程,倒不构成问题。真要说起来,我觉得协程的最大的好处是在写过程式(命令式)风格的代码时,很好的简化了逻辑的流程。

区分协程和这类普通functor的关键在于,协程在yield的时候,他的一项工作并没有完成,下次重入的时候继续; 而functor虽然也可以用来实现coroutine,但是一般的用法里面,一次functor调用,就是完成一项相对独立的任务。这个普通functor任务过程中,可能改变了内部的变量影响到下次重入。但是影响下次调用并非是协程的目的,协程的设计目的就是跟线程一样,可以一项任务中途让出资源,下次继续执行。重入是实现这个“继续执行”的方式。
协程事实上实现了某种意义上的并行,I/O和cpu之间的并行。逻辑上个人觉得也和线程更接近,需要调度。协程避免了竞态和锁这类复杂的问题,很大一个原因是因为在cpu上是单线程,相对于线程并非什么特别的优点。只要一个cpu的话,多线程一样有办法可以不管竞态和锁,python的GIL就是个例子
虚拟机可分为两种:基于堆栈的(Stack-based ) 和基于寄存器(Register-based) 的虚拟机。基于堆栈的虚拟机也定义了少量的寄存器,基于寄存器的虚拟机也有堆栈,其区别体现在它们提供的指令集体系结构(ISA ,Instruction Set Architecture) 。ISA 是处理器的一部分,对于编译器实现者和程序员是可见的,ISA 是硬件和软件之间的接口。基于堆栈的虚拟机的指令比基于寄存器的指令要小,因为在指令中不需要指定操作数。基于堆栈的虚拟机使用堆栈来保存中间结果、变量等,基于寄存器的虚拟机则支持寄存器的指令操作。基于堆栈的虚拟机需要用Push 、Pop 来传送数据,通常,完成同样的工作,基于寄存器的虚拟机所采用的指令数比基于堆栈的虚拟机采用的指令数目少,可以提高执行效率。例如,将语句 C=A+B转化为中间代码,

堆栈虚拟机指令很低级,基于寄存器的处理器有更强大的指令功能,而且易于调试。

基于堆栈的处理器在处理函数调用、解决递归问题和切换上下文时简单明快。

采用寄存器架构时,虚拟机需要经常保存和恢复寄存器中的内容,还要考虑对操作数的寻址问题等,因此,基于堆栈的虚拟机实现起来更简单,基于寄存器的虚拟机能提供更强大的指令集。

大多数虚拟机是基于堆栈的,如Pascal’s P-machine 、Java 的JVM和微软的.Net 环境。当前流行的 Lua 脚本语言,5.0之前的版本也是基于堆栈的,5.0之后改为基于寄存器实现。虚拟机实现越复杂,解密者也就越难破解,但是其实现成本也高。

基于寄存器的虚拟机需要用寄存器来保存中间结果、变量等,而基于堆栈的虚拟机使用堆栈即可。例如,针对同一条指令:Add,基于堆栈的处理器的首先从堆栈里Pop两个数,然后将两数相加,再把和Push到堆栈,Add指令只占用1个字节,而基于寄存器的处理器的对应指令为AddR1,R2,113,Add指令至少要占用4个字节。通过以上比较可以看出,基于堆栈的体系架构实现较为简单,但其运行效率不如基于寄存器的,而基于寄存器的体系结构在编译后的代码结构上又不如基于堆栈的简洁。对于同一个程序,为基于堆栈的机器编译出来的版本要比为基于寄存器的机器编译出来的版本小好几倍。
基于堆栈的指令集能够让字节码更紧凑,可以提高Cache的利用率,而且实现起来也比基于寄存器的机器要简单。而基于寄存器的虚拟机虽然代码大小有所增加,但是带来的性能提升更加突出。



0 0
原创粉丝点击