并发模型中的CSP模型

CSP模型

CSP模型是上个世纪七十年代提出的,用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信的并发模型。 CSP中channel是第一类对象,它不关注发送消息的实体,而关注与发送消息时使用的channel。 除了CSP模型之外,还有另一种叫做actor的模型,完全没有接触过,之后慢慢学。

另:第一类对象(first-class citizen)在计算机中指可以在执行期创造并作为参数传递给其他函数或存入一个变量的实体。 绝大多数语言中,数值与基础型别都是第一类对象,然而不同语言中对函数的区别很大,例如C语言与C++中的函数不是第一类对象,因为在这些语言中函数不能在执行期创造,而必须在设计时全部写好。相比之下,Scheme中的函数是第一类对象,因为可以用lambda语句来创造匿名函数并作为第一类对象来操作。

Golang CSP

Golang 就是借用CSP模型的一些概念为之实现并发进行理论支持,其实从实际上出发,go语言并没有,完全实现了CSP模型的所有理论,仅仅是借用了 process和channel这两个概念。process是在go语言上的表现就是 goroutine,是实际并发执行的实体,每个实体之间是通过channel通讯来实现数据共享。

这里需要提到的是goroutine的实现: goroutine底层是通过协程(coroutine)实现并发的。关于协程、线程和进程的关系之后再详细研究。使用协程具有以下优势:

  1. 用户空间 避免了内核态和用户态的切换导致的成本
  2. 可以由语言和框架层进行调度
  3. 更小的栈空间允许创建大量的实例

另外,golang为Goroutine提供语言层面的调度器,实现了高效率m:n的线程对应关系。Golang为了调度的公平性,在调度器加入了steal working 算法 ,在一个P自己的执行队列,处理完之后,它会先到全局的执行队列中偷G进行处理,如果没有的话,再会到其他P的执行队列中抢G来进行处理。

图中:

  • M:是内核线程
  • P : 是调度协调,用于协调M和G的执行,内核线程只有拿到了 P才能对goroutine继续调度执行,一般都是通过限定P的个数来控制golang的并发度
  • G : 是待执行的goroutine,包含这个goroutine的栈空间
  • Gn : 灰色背景的Gn 是已经挂起的goroutine,它们被添加到了执行队列中,然后需要等待网络IO的goroutine,当P通过 epoll查询到特定的fd的时候,会重新调度起对应的,正在挂起的goroutine。

alt text