从Erlang进程看协程思想
多核慢慢火了以后,协程类编程也开始越来越火了。比较有代表性的有Go的goroutine、Erlang的Erlang进程、Scala的actor、windows下的fibre(纤程)等,一些动态语言像Python、Ruby、Lua也慢慢支持协程。
其实我们听过协程相关很多名词,下面大致来解释一下:
- OS进程: 进程是资源管理的最小单元,包括进程控制块(PCB)、程序段、数据段
- OS线程: 线程是程序执行的最小单元,由线程ID,当前指令指针(PC),寄存器集合和堆栈组成;内核线程之间切换的时候需要在内核态进行上下文和堆栈切换。
- 轻量级进程(LWP): 其实是基于内核线程的高级抽象,每个LWP对应一个实际的线程。优点是在某些情况下不需要新建一个实际进程那么大开销。
- 协程: 在不同的编程语言或库里因为实现方式差异,也叫纤程、actor等。实际可以理解为应用程序级别的线程,其调度器不是内核的CPU,而是由用户级别实现的VM级别调度器。
也就是说,协程具有以下特点:
- 协程并发时的切换基本在用户态级别切换,不会触发内核,减少系统开销
- 尽管协程实际肯定还是在某个OS线程上执行,但是如何执行、何时执行、在哪个线程上执行等都是由用户的调度器去决定的,即协程使用者无需关心底层线程的情况。
- 协程作为用户级别的存在,实际就是一段待执行的函数,创建销毁开销很小;且因为切换基本在用户态,所以可以很轻易地在一个进程内开百万级别的协程。
基本能满足以上特点的我们都可以称其为协程(coroutine)。
但不同语言却有不同实现方式,其中主要有以erlang为代表的 actor model,还有以Go为代表的Coroutine。
Go那边不太熟悉,不过主要就是协程间通信方式和yield主动让出CPU操作,这些在Python、Ruby等中也有实现。
Erlang以及Scala主要采取的是一种 actor model,也叫面向actor的编程。主要遵循以下特点:
- 一切皆Actor的思想,即每一个执行者都当做一个actor。(有点像Java每一个功能承担者都是一个对象类)
- Actor之间只能允许通过消息传递来通信,不共享内存,且消息传递无副作用(无副作用指不会被脏读或修改等问题困扰)。
- Actor的收发消息是异步的,接收到的消息一般是一份副本。
从他的特性看来,Actor是一种比普通Coroutine更彻底的一种面向并发的编程模型。在设计actor model的程序时,开发者只需要考虑如何利用并发特性使自己程序的并发性能更好,而不需要去考虑锁、冲突、不一致性等问题。