Skip to content

Latest commit

 

History

History
561 lines (534 loc) · 29.3 KB

README.md

File metadata and controls

561 lines (534 loc) · 29.3 KB

第十部分 编译原理

前言

如何理解编译器?

编译器能帮助我们做哪些事情?

注意事项

请基于macos或者linux发行版构架学习环境

相关文档

Java 内存模型 内存屏障的说明

笔记【更新中】

现代语言设计

  • 功能完备的语言包含了

    • 编译器
    • 运行时
      • 内存管理
      • 并发机制
      • 解释器
      • ...
    • 标准库
      • 算术计算
      • 字符串处理
      • 文件读写
      • ...
  • 如何设计一门计算机语言

    • 清晰的需求
    • 良好的设计
    • 历史中的语言往往都是一个流行的系统的脚本
    • 设计问题
    • 案例
      • js
        • 解释执行的语言
        • 嵌入到 HTML 中 ,方便下载和执行
        • 跨平台
        • 对编译速度有要求
          • 词法分析
          • 懒解析
  • 现代语言篇

    • 各门语言的编译器的前端、中端和后端技术做对比和总结
    • 对语言的运行时和标准库的实现技术做解析
      • 对语言的运行时和标准库的宏观探讨
      • 是垃圾收集机制
      • 是并发模型
    • 计算机语言设计上的 4 个高级话题
      • 元编程技术
      • 泛型编程技术
      • 面向对象语言的实现机制
      • 函数式编程语言的实现机制
  • 前端编译技术

    • 手写词法编译器
      • 特点
        • 实现较简单
        • 便于做优化
        • 便于处理特殊情况
          • 例如 mysql 词法分析器处理字符集下某个字符串是否是合法的token
    • 自顶向下分析为主的语法分析器
      • 手写 vs 工具生成
      • 差异化的语义分析功能
      • 友好的语言特性
        • 自动类型推导
          • 自动类型推导可以减少编程时与类型声明有关的工作量
          • C++ 11 中,采用了 auto 关键字做类型推导。
          • Kotlin 中用 var 声明变量,也支持显式类型声明和类型推导两种方式
          • Go 语言,会用“:=” 让编译器去做类型推导
          • Java 语言也在 Java 10 版本加上了类型推导功能
        • Null 安全性
        • Null 引用其实是托尼·霍尔(Tony Hoare)在 1960 年代在设计某一门语言(ALGOL W)时引入的,后来也纷纷被其他语言所借鉴。
        • 以 Kotlin 为例,在缺省情况下,它不允许你把 Null 赋给变量,因此这些变量就不需要检查是否为 Null。
        • 安全调用(Safe Call)
          • 采用“?.”操作符来访问
        • Null 安全性在编译器里应该怎样实现呢
          • 友好的语法糖
            • 分号推断
            • 单例对象
            • 纯数据的类
              • jdk14 支持 Record 类
            • 没有原始类型,一切都是对象
          • 一些友好的词法规则
            • 嵌套的多行注释
            • 标识符支持 Unicode
              • 你可以用中文来声明变量和函数名称
            • 多行字符串字面量
  • 中端编译技术 IR 与 SSA

    • SSA
      • 源代码中的一个变量,会变成多个版本,每次赋值都形成一个新版本
      • SSA 中,它们都叫做一个值(Value
      • 对变量的赋值就是对值的定义(def
      • 定义出来之后 可以在定义其他值的时候被使用(use)
      • 形成 use-def 链
      • 特点
        • 定义未被使用--死代码删除
        • 常数折叠,顺着链,替换值,形成常数传播
        • 定义相同,值相同,可使用公共子表达式消除
    • Sea of Nodes 的特点总结
      • 特点
        • 是把数据流图和控制流图合二为一,从而更容易实现全局优化
        • 在生成图的过程中,顺带就可以完成很多优化了
        • 从高到低的多层次 IR
          • 对一个数组元素的访问
          • 访问一个对象的成员变量
          • 本地变量的访问
  • 优化算法的总结

    • 编译器基于IR的处理
      • 层层地做 Lower
      • 对 IR 做分析
      • 实现各种优化算法
    • 特点
      • 有些基本的优化,是每个编译器都会去实现的
      • 对于解释执行的语言,其编译器能做的优化是有限的
        • Python 类型检查都是在运行期进行
          • 根据不同的类型执行不同的功能
          • 所有对象都是在堆中申请的
        • JVM
          • 编译器已做类型检查
          • 简单优化 没消除无用代码
          • 内联优化和逃逸分析没有做
        • V8 的 Ignition 解释器
          • 在寄存器利用方面比JAVA更有优势
          • 但是动态类型拖了后腿
    • 动态类型语言
      • 优化编译首要任务是类型推断
      • 要为自己可能产生的推理错误做好准备,在必要的时候执行逆优化功能。
      • Julia 也是动态类型的语言,但它采取了另一个编译策略。它会为一个函数不同的参数类型组合,编译生成对应的机器码。
    • JIT 编译器可以充分利用推理性的优化机制
    • 对于静态类型的语言,不同的编译器的优化程度也是不同的
  • 后端编译总结

    • 关键算法
      • 指令选择
        • 模式树(Pattern Tree)
        • 对一个 AST 生成指令,就是用这样的模式树或瓦片来覆盖整个 AST 的过程。所以,这样的算法也叫做基于模式匹配的指令生成算法。
        • #4(Store_Offset)
          • 比如你在对象地址上加一个偏移量,就能获得成员变量的地址,并把数值保存到这个地址上
        • #9(Lea) 相当于 x86 指令集中的 Lea 指令
        • 实现的算法
          • 第一种
            • 我们采取深度优先的后序遍历,也就是按照“左子节点 -> 右子节点 -> 父节点”的顺序遍历,针对每个节点去匹配上面的模式。
            • 这种方法,是自底向上的做树的重写。
          • 第二种
            • 类似 Graal 编译器所采用的方法,自顶向下的做模式匹配
            • 是找出用模式匹配来覆盖 AST 的所有可能的模式,并找出其中 Cost 最低的
          • 第三种
            • BURS 算法
      • 寄存器分配
        • 线性扫描算法并不能获得寄存器分配的最优解
        • 线性扫描算法可以采用一些策略,让一些使用频率低的变量被溢出,而像高频使用的循环中的变量,就保留在寄存器里
        • 还有一些其他提升策略。比如,当存在多余的物理寄存器以后,还可以把之前已经溢出的变量重新复活到寄存器里。
        • 思路
          • 线性扫描整个代码,并给活跃变量分配寄存器。如果物理寄存器不足,那么就选择一个变量,溢出到内存中
      • 指令排序
  • 编译器后端与语言的设计 -因素

    • 平衡编译速度和优化效果
    • 确定所支持的硬件平台
    • 设计后端 DSL
  • 运行时

    • 一门语言的构成
    • 编译器
    • 运行时
      • 每种语言都有特定的执行模型
        • 执行模型需要运行时系统(Runtime System)的支持
          • 简称为 “运行时”
      • 包含的主要功能
        • 程序运行机制
        • 内存管理机制
        • 并发机制
      • java 运行时
        • JVM 规定了一套程序的运行机制
          • 定义了一套字节码来运行程序
          • 规定了一套类型系统,包括基础数据类型、数组、引用类型等
          • 定义了 class 文件的结构
          • 提供了一个基于栈的解释器,来解释执行字节码
          • JVM 还支持即时编译成机器码并执行的机制
          • Java 程序之间的互相调用,需要遵循一定的调用约定或二进制标准,包括如何传参数等等
        • JVM 对内存做了统一的管理
          • 把内存划分为程序计数器、虚拟机栈、堆、方法区、运行时常量池和本地方法栈等不同的区域
        • JVM 封装了操作系统的线程模型,为应用程序提供了并发处理的机制
          • JVM 实际上提供了一个基础的对象模型,JVM 上的各种语言必须遵守
          • 基于 JVM 的语言程序要去调用 C 语言等生成的机器码的库,会比较难。
          • 在内存管理上,程序不能直接访问内存地址,也不能手动释放内存
          • 在并发方面,JVM 只提供了线程机制。
      • python 运行时
        • Python 也提供了一套字节码,以及运行该字节码的解释器
        • 字节码中操作的那些标识符,都是 Python 的对象引用。
        • 在内存管理方面,Python 也提供了自己的机制,包括对栈和堆的管理
        • Python 运行程序的时候,有些时候是运行机器码,比如内置函数,而有些时候是解释执行字节码。
        • 栈帧跟 C 语言程序的栈帧是没啥区别的
        • 解释器本身主要就是一个 C 语言实现的函数,而操作数栈就是这个函数里用到的本地变量。 因此操作数栈也会像其他本地变量一样,被优化成尽量使用物理寄存器,从而提高运行效率。
        • 栈桢中的操作数栈,其实是有可能基于物理寄存器的。
        • Python 还提供了对堆的管理机制。
        • 通过 Python 提供的一个 Arena 机制,使得内存的申请和释放更加高效、灵活
        • Python 还提供了基于引用的垃圾收集机制
        • Python 把操作系统的线程进行了封装. 让 Python 程序能支持基于线程的并发。同时,它也实现了协程机制
      • C、C++、Go 的运行时
        • C 语言最主要的运行时,实际上就是操作系统
          • 深入使用 C 语言,某种意义上就是要深入了解操作系统的运行机制。
        • C 没有自动内存管理机制
        • C 可以直接使用线程机制 但是操作系统没有提供协程和Actor机制,C 没有这个机制
        • 不过有一个程序 crt0.o,有时被称作是 C 语言的运行时
        • Go 语言虽然也是编译成二进制的可执行文件,但它的运行时要复杂得多
        • Go 语言最显著的特点是提供了自己的并发机制,也就是 goroutine。
        • 在 Android 平台上,你可以把 Java 程序以 AOT 的方式编译成可执行文件
    • 标准库
      • 库和标准库
        • 根据库的使用场景和与编译器的关系
        • 标准库
          • 标准库,供用户的程序调用。
        • 运行时库
          • 运行时库,它们不是由用户直接调用的,而是运行时的组成部分。
        • 内置函数
          • Built-in 或者 Intrincics 的内置函数,它们是用来辅助生成机器码的。
    • 标准库的特殊性
      • 有的库可以用本语言来实现,而有的库必须要用其他语言来实现
      • 第二,标准库的接口不可以经常变化,甚至是要保持一直不变
      • ,标准库往往集中体现了一门语言的核心特点。
    • 标准库需要包含什么功能?
      • 包含 IO 功能,包括文件 IO、网络 IO
        • 终端本身就相当于一个文件,这实际上是用了文件 IO 功能。
      • 支持内置的数据类型。
      • 支持各种容器型的数据结构。
      • 对日期、图形界面等各种不同的功能支持
  • 垃圾收集算法

    • 常见的垃圾收集算法
      • mark and sweep - 标记-清除

        • 从GC根节点出发,顺着对象的引用关系,依次标记可达的对象
          • GC根节点
            • 全局变量
            • 常量
            • 栈内的本地变量
            • 寄存器中的本地变量
        • 剩下的对象,就是内存垃圾
      • mark and compact - 标记-整理

        • 标记-清除 算法运行时间长了之后,会形成内存碎片
        • 申请任意大于碎片最大值的内存空间都会不足
        • 使用标记-整理算法可以将存活的对象整理至一边,消除掉内存碎片
      • stop and copy - 停止-拷贝

        • 将内存分为新旧两个空间
        • 堆指针指向自由空间的开始位置
        • 申请空间时,指针向右移动即可
      • 引用计数

        • 在对象中保存该对象被引用的数量,一旦这个引用数为零,那么可以作为垃圾被收集
        • 自动引用计数 ARC
          -优点   - 不需要为了垃圾收集而专门停下程序 -缺点   - 不能处理循环引用(Reference Cycles)的情况
    • 内存垃圾
      • 保存在堆里的,无法从程序访问的对象
      • 所有不可达的内存为内存垃圾
    • 分代收集
      • 新创建的对象往往会很快死去
      • 使用临时变量指向一些新创建的对象,这些对象大多数在退出方法时,就没用了 
      • 新生代频繁回收
        • 适合复合式收集算法
      • 老一代生命周期较长
        • 适合标记-清除或标记-整理算法
    • 增量收集、并发收集
      • 增量收集每次只完成部分收集工作,没必要一次把活干完,从而减少停顿。
      • 并发收集
        • 不影响程序执行的情况下,并发执行 GC
  • python与引用计数算法

    • 使用相同语言的算法的语言有
      • Swift
      • 方舟编译器
    • 内存管理和 GC 机制
      • 每个数据都是对象
      • 对象申请于堆
      • c、java 本地变量申请于栈
      • python申请的对象有个固定的开销
      • 栈的优势
        • 不会产生内存碎片
        • 数据局部性好
        • 数据收放自如
      • 堆的劣势
        • 太多的小对象
        • 太多系统调用
        • 性能消耗大
        • 易产生内存碎片
        • 数据局部性差
    • 基于区域(Region-based)的内存管理方法
    • Python 每次都申请一大块内存,这一大块内存叫做 Arena
    • 需要较小内存时,直接从 Arena 上划拨
    • 需要归还内存的时候,不一定直接归还给操作系统,而是归还给 Arena
    • 每个 PyObject 都有一个 obj_refcnt
      • 解释器执行字节码的时候,会根据不同的指令自动增加或者减少 ob_refcnt 的值
      • obj_refcnt 值为0时,可清除
    • 其他语言的垃圾收集
      • 采用像停止 - 拷贝这样的算法,其实是用空间换时间,以更大的内存消耗换来性能的提升。
      • 这其实也是为什么 Android 手机比 iPhone 更加消耗内存的原因之一。
  • 为什么在垃圾收集时,要停下整个程序?

    • 采用标记 - 清除算法时,你就必须要停下程序
    • 你必须从所有的 GC 根出发,去找到所有存活的对象,剩下的才是垃圾
    • 你不仅要停下当前的线程,扫描栈里的所有 GC 根,你还要停下其他的线程
    • 如果垃圾收集算法需要做内存的整理或拷贝,那么这个时候仍然要停下程序
  • 如何能减少停顿时间?

    • 第一招,分代收集可以减少垃圾收集的工作量
    • 第二招,可以尝试增量收集。
    • 三色标记法
      • 特点
        • 黑色对象永远不能指向白色对象
        • 色标记法中,黑色的节点是已经处理完毕的,灰色的节点是正在处理的。如果灰色节点都处理完,剩下的白色节点就是垃圾。
    • 编译器做什么配合?
      • 编译器需要往生成的目标代码中插入读屏障(Read Barrier)和写屏障(Write Barrier)的代码
    • 第三招:并发收集。
      • 除了少量的时候需要停下整个程序(比如一开头处理所有的 GC 根),其他时候是可以并发的,这样就进一步减少了总的停顿时间。
  • 并发中编译技术

    • 并发需求增长的因素
      • CPU 在制程上的挑战越来越大
      • 现代应用对并发处理的需求越来越高
    • 并发技术
      • 线程
      • 协程
      • Actor 模式
    • 并发的底层机制:并行与并发、进程与线程
      • 超线程(Hyper Threading)技术
        • 让一个内核可以同时执行两个线程,增加对 CPU 内部功能单元的利用率
        • 操作系统会用分时技术,让一个程序执行一段时间,停下来,再让另一个程序运行
          • 由操作系统的一个调度程序(Scheduler)来实现的
            • 以进程为单位来做调度,开销比较大。
            • 切换进程的时候,要保存当前进程的上下文,加载下一个进程的上下文,也会有一定的开销。
              • 用户级上下文
              • 寄存器上下文
              • 系统及上下文
      • 线程技术
        • 一个进程内部,可以有多个线程,每个线程都共享进程的资源
          • 内存资源
          • 操作系统资源
          • 安全属性
          • 栈和寄存器资源
      • 线程调度原理
        • 操作系统会让一个线程运行一段时间,然后把它停下来,把它所使用的寄存器保存起来,接着让另一个线程运行
      • 把进程作为资源分配的基本单元,而把线程作为并发执行的基本单元
        • 进程作为并发基础
          • google chrome
            • 每打开一个 Tab 页,就新启动一个进程
            • 浏览器中多个进程之间不需要有互动
            • 各个进程所使用的资源是独立的
            • 一个进程崩溃也不会影响到另一个
        • 线程作为并发基础
          • 轻量级,消耗的资源比较少
          • 在一般的网络编程模型中,我们可以针对每个网络连接,都启动一条线程来处理该网络连接上的请求
          • 采用线程模型的话,程序就可以在不同线程之间共享数据
            • SQL 案例
              • 一个客户端提交了一条SQL之后,查询结果可以缓存起来。
              • 如果另一个用户恰好也执行了同一个SQL,那么可以直接利用缓存
          • 共享内存也会带来一些问题
            • 多个线程访问同样的数据的时候,会出现数据处理的错误。如果使用并发程序会造成错误
            • Java 语言内置的并发模型就是线程模型
  • Java 的并发机制

    • 语言层面对并发编程的支持
    • Thread
    • Runnable 实现类
    • 相关的关键字
      • synchronized
      • volatile
        • 它解决的是变量的可见性问题
        • 线程1写入线程2读取不到问题
          • 因为CPU的高速缓存,线程1写入不会立即回写到内存
          • volatile
          • 让程序在访问被修饰的变量的内存时,让其他处理器能够见到该变量最新的值
          • 当某个线程写数据的时候,要写回到内存,而不仅仅是写到高速缓存;当读数据的时候,要从内存中读,而不能从高速缓存读。
          • 内存屏障(Memory Barriers)
            • LoadLoad 屏障、StoreStore 屏障、LoadStore 屏障和 StoreLoad 屏障
    • 养成直接看字节码、汇编码来研究底层机制的习惯,
    • 两个特殊的指令
      • monitorenter
        • 试图获取某个对象引用的监视器(monitor)的所有权
      • monitorexit
    • 我们用了锁的机制,保证被保护的代码块在同一时刻只能被一个线程访问,从而保证了相关操作的原子性。
      • cmpxchg 指令
        • 通过一条指令,完成比较和交换的操作
        • 作用
          • 通过这样一条指令,计算机就能支持原子操作。
        • 实际执行的时候,r10 中的值并不是简单的 0 和 1,而是获取了 Java 对象的对象头,并设置了其中与锁有关的标志位。
        • lock 前缀
          • 让这条指令在同一时间,只能有一个内核去执行。
  • 协程的特点和使用场景

    • 1958 马尔文 · 康威(Melvin Conway)提出
    • 20 世纪 60 年代高德纳(Donald Ervin Knuth)总结为两种子过程(Subroutine)的模式之一。
    • 函数调用
    • 协程
      • 一般是程序交出运行权,之后又被另外的程序唤起继续执行
      • 线程是通过操作系统的调度器来控制的
      • 在 JVM 中,缺省会为每个线程分配 1MB 的内存,用于线程栈
    • 别名
      • 绿色线程、纤程
      • 共同点
        • 协程占用的资源非常少
        • 协程是用户自己的程序所控制的并发。
    • 协程和函数调用的区别
      • 使用协程跟我们平常使用函数几乎没啥差别
      • 函数调用时,调用者跟被调用者之间像是一种上下级的关系
      • 在协程中,调用者跟被调用者更像是互相协作的关系
        • 例如 一个生产者 一个消费者
    • 使用场景
      • 生产者和消费者模式 生成器 场景
        • 消息队列编程 多线程
        • Unix 管道 多线程
        • 协作关系
          • 词法分析器
          • 语法分析器
          • 语法分析器消费1个 词法分析器 生产一个
      • IO 密集型的应用
      • 协程在发起请求之后就把控制权交出,调度程序接收到数据之后再重新激活协程,这样就能高效地完成 IO 操作
      • 微服务架构的应用
      • 近年来异步编程模型
        • Node.js
        • 网络通讯等 IO 操作不必阻塞线程,而是通过回调来让主程序继续执行后续的逻辑。
        • 易产生回调地狱
      • 协程可以让你用自己熟悉的命令式编程的风格,来编写异步的程序
        • 协程用于同步和异步编程的时候,其调度机制是不同的
        • 异步调用
          • 把异步 IO 机制与协程调度机制关联起来
    • 协程的运行原理
      • 函数是调用的栈帧
      • 协程
        • 程序可以从堆里申请一块内存,保存协程的活动记录,包括本地变量的值、程序计数器的值(当前执行位置)等等
        • 把活动记录保存到堆里 类似于闭包函数
        • 闭包函数
          • 也需要在堆里保存闭包中的自由变量的信息,并且在下一次调用的时候,从堆里恢复
          • 闭包不需要保存本地变量,只保存自由变量就行了;也不需要保存程序计数器的值,因为再一次调用闭包函数的时候,还是从头执行
          • 协程则是接着执行 yield 之后的语句。
      • 协程的调度,包括协程信息的保存与恢复、指令的跳转,需要编译器的帮忙吗
        • 对于C和C++
          • 可以用 setjmplongjmp 等函数
          • 用库实现,通常要由程序管理哪些状态信息需要被保存下来
            • 可能要专门设计一个类型,来参与实现协程状态信息的维护。
        • 编译器帮忙
          • 自动确定需要保存的协程的状态信息,并确定需要申请的内存大小
          • 一个协程和函数的区别,就仅仅在于是否使用了 yield 和 co_return 语句而已
    • StackfulStackless 的协程
      • 协程和函数的相似性
        • 主程序调用,执行完成后控制权给主程序
        • 使用栈来管理本地变量和参数信息
          • 栈未完全运行完毕之后,使用堆保存活动记录
        • 协程中可调用其他函数
      • 如何解决两级或多级调用的问题?
        • 协程的逐级调用过程,形成了自己的调用栈,这个调用栈需要作为一个整体来使用,不能拆成一个个单独的活动记录
        • 添加一个辅助栈 Side Stack
        • 需要一个辅助的栈来运行协程的机制,叫做 Stackful Coroutine
        • 在主栈上运行协程的机制,叫做 Stackless Coroutine
          • Stackless
            • 只能在顶层的函数里把控制权交回给调用者
            • Stackless 的协程用的是主线程的栈,也就是说它基本上会被绑定在创建它的线程上了
            • Stackless 的协程的生命周期受制于他们的创建者生命周期
          • Stackful
            • 可以在协程栈的任意一级,暂停协程的运行。
            • Stackful 的协程,可以从一个线程脱离,附加到另一个线程上
            • 生命周期可以超过创建者的生命周期
      • 不同语言的协程实现差异
        • c++
          • C++20 标准中,增加了协程特性。
          • 采用了微软的 Stackless 模式。
          • 关键字
            • co_await
            • co_yield
            • co_return
          • 库实现
            • 腾讯的微信团队就开源了一套协程库,叫做 libco
            • libco 发明了共享栈的机制
            • 用同步的编程风格来实现异步的功
        • python
          • generator
          • 3.4 后 使用 async await
        • java
        • Java 原生是不支持协程的
        • 几种方法可以让其支持
          • 虚拟机补丁
          • 字节码操纵,从而改变 Java 缺省的控制流执行方式,并保存协程的活动记录。
          • 基于 JNI。
          • 把线程封装成协程
        • javascript
          • es6 generator
          • es7 async await
        • julia 、 go
          • 对称的协程机制
          • 多个协程可以通过 channel 通讯,null或Chanel已满自动停止
          • Goroutine 是 Stackful 还是 Stackless?
            • Stackful
            • 优点
              • 生命周期可超过创建者
              • 可在线程间转移
            • 缺点
              • 预分配较多的内存用作协程的栈空间
                • go routine 2k
                • libco 128 k
                • 区分
                  • go routine 有编译器的支持
                    • 编译器可以为每个函数生成这样的序曲代码
            • channel 机制
              • 在同一个时刻,只有一个 Goroutine 能够读写 channel
              • channel 在底层也采用了锁的机制
            • 协程的调度时机
              • 对于 generator 类型的协程,基本上是同步调度的,
              • 第二个调度机制,是跟异步 IO 机制配合
              • 像线程那样的抢占式(preemptive)的调度
              • 由于有编译器的参与,这种类似抢占的逻辑是可以实现的
              • 协程与异步 IO 结合是一个趋势。
  • 什么是 Actor 模型?

    • Actor 模型是 1973 年由 Carl Hewitt 提出的
    • Actor 模型中,并发的程序之间是不共享内存的
    • 通过互相发消息来实现协作
    • 每个 Actor 都有一个邮箱,用来接收其他 Actor 发来的消息
      • 发完即回,异步交互
      • 如果 a - > b , b 需要返回消息,那么 b 也是通过发送消息的方式
    • 不共享内存,能解决传统上需要对资源做竞争性访问的需求吗?
      • 电影院卖票功能
        • 线程或协程,会用加锁的方式实现同步互斥,但多个程序是串行的,因此系统性能差
        • Actor 模式
          • 由于 Actor 之间不共享任何数据,因此不仅增加了数据复制的时间,还增加了内存占用量
          • 基于消息的并发机制,基本上是采用异步的编程模式
          • 支持 Actor 的最有名的语言是 Erlang。
            • 特点
              • 对并发的支持非常好,所以它也被叫做面向并发的编程语言(COP)
              • 用 Erlang 可以编写高可靠性的软件
            • 模式
              • Erlang 的软件由很多 Actor 构成;
              • Actor 可以分布在多台机器上
              • 一个 Actor 甚至机器出现故障,都不影响整体系统,可以在其他机器上重新启动该 Actor;
            • 每个 Actor 叫作一个进程(Process
              • ProcessErlang 运行时的并发调度单位。
              • Actor 的代码可以在运行时更新
            • 就像生命体与细胞的关系
            • 案例
              • 消息队列系统 RabbitMQ
              • 分布式的文档数据库系统 CouchDB

版权声明

引用内容版权归 【极客时间】 所有