Golang CSP concurrency model

Golang CSP concurrency model


Today, I will introduce the concurrency mechanism of go language and the CSP concurrency model it uses.

CSP concurrency model

The CSP model was proposed in the 1970s to describe a concurrency model in which two independent concurrent entities communicate through a shared communication channel (pipe). Channel in CSP is the first type of object, it does not pay attention to the entity sending the message, but pays attention to the channel used when sending the message.

Golang CSP

Golang borrows some concepts of the CSP model to provide theoretical support for concurrency. In fact, from the point of view of reality, the go language does not fully implement all the theories of the CSP model, but only borrows the two concepts of process and channel. The performance of process in the go language is that goroutine is an entity that actually executes concurrently, and each entity communicates through channels to achieve data sharing.

Channel

Golang uses the concept of channel in CSP. The channel is created separately and can be passed between processes. Its communication mode is similar to the boss-worker mode. An entity processes by sending a message to the channel and then listening to the entity of the channel. The two entities are Anonymous, this realizes the decoupling between entities, in which channel is a synchronous message sent to the channel, which must eventually be consumed by another entity. In principle, it is a blocking message queue.

Goroutine

Goroutine is the entity that actually executes concurrently. Its bottom layer uses coroutines to achieve concurrency. Coroutines are user threads that run in user mode, similar to greenthreads. The starting point for the bottom layer of go to use coroutines is that it has the following characteristics :

  • User space avoids the cost of switching between kernel mode and user mode
  • Can be scheduled by the language and framework layers
  • Smaller stack space allows creating a large number of instances

可以看到第二条 用户空间线程的调度不是由操作系统来完成的,像在java 1.3中使用的greenthread的是由JVM统一调度的(后java已经改为内核线程),还有在ruby中的fiber(半协程) 是需要在重新中自己进行调度的,而goroutine是在golang层面提供了调度器,并且对网络IO库进行了封装,屏蔽了复杂的细节,对外提供统一的语法关键字支持,简化了并发程序编写的成本。

Goroutine 调度器

上节已经说了,golang使用goroutine做为最小的执行单位,但是这个执行单位还是在用户空间,实际上最后被处理器执行的还是内核中的线程,用户线程和内核线程的调度方法有:

  • N:1 多个用户线程对应一个内核线程


  • 1:1 一个用户线程对应一个内核线程


  • M:N 用户线程和内核线程是多对多的对应关系


golang 通过为goroutine提供语言层面的调度器,来实现了高效率的M:N线程对应关系

调度示意

图中

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

Golang为了调度的公平性,在调度器加入了steal working 算法 ,在一个P自己的执行队列,处理完之后,它会先到全局的执行队列中偷G进行处理,如果没有的话,再会到其他P的执行队列中抢G来进行处理。

总结

Golang实现了 CSP 并发模型做为并发基础,底层使用goroutine做为并发实体,goroutine非常轻量级可以创建几十万个实体。实体间通过 channel 继续匿名消息传递使之解耦,在语言层面实现了自动调度,这样屏蔽了很多内部细节,对外提供简单的语法关键字,大大简化了并发编程的思维转换和管理线程的复杂性。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325780863&siteId=291194637