文章目录
Go Channel
Channel是Go中的一个核心类型,可以理解为一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。
操作符:<-
(右写左读)
Go CSP并发模型
CSP介绍
传统的并发模型主要分为Actor模型和CSP模型,CSP全程为communicating sequential processes,CSP模型由并发执行实体(进程,线程或协程),和消息通道组成,实体之间通过消息通道发送消息进行通信。和Actor模型不同,CSP模型关注的是消息发送的载体,即通道,而不是发送消息的执行实体。其中执行实体对应的是goroutine,消息通道对应的就是channel。
Channel
channel在go中是被单独创建并且可以在进程之间传递,他的通信模式类似于 boss-worker 模式,一个实体通过将消息发送到channel中,然后又监听这个channel的实体处理,两个实体之间是匿名的,这个就是实现实体中间的解耦,其中channel是同步的一个消息被发送到channel中,最终一定是要被另外的实体消费掉的,在实现原理上其实就是一个阻塞的消息队列。
Goroutine
Goroutine是实际并发执行的实体,它底层使用协程(coroutine)实现并发,coroutine是一种运行在用户态的用户线程,go底层选择使用coroutime的出发点是因为以下三大特点:
- 用户空间 避免了内核态和用户态的切换导致的成本
- 可以由语言和框架层进行调度
- 更小的栈空间允许创建大量的实例
由2可知用户空间线程的调度不是由操作系统来完成的,go提供了调度器并对网络IO库进行封装,屏蔽了复杂的细节,对外提供统一的语法关键字支持,简化了并发程序编写的成本。
Goroutine 调度器
go使用goroutine作为最小的执行单位,但是这个执行单位还在用户空间,实际上最后被处理器执行的还是内核中的线程,用户线程和内核线程的调度方法有:
-
多个用户线程对应一个内核线程
-
一个用户线程对应一个内核线程
-
用户线程和内核线程多对多
go通过为goroutine提供语言层面的调度器,来实现高效率的多对对线程对应关系
- M:内核线程
- P:调度协调,用于协调M与G的执行,内核线程只有拿到了P才能对goroutine继续调度执行,一般都是通过限定P的个数来控制go的并发度
- G:待执行的goroutine,包含这个goroutine的栈空间
- Gn:灰色背景的Gn是已经挂起的goroutine,他们被添加到了执行队列中,然后需要等待网络IO的goroutine,当P通过epoll查询到特定的fd时,会重新调度对应的正在挂起的goroutine
go为了调度的公平性,在调度器中加入了steal working算法,在一个P自己的执行队列中,处理完之后,它会先到全局的执行队列中偷G进行处理,如果没有的话,再去其他P的执行队列中偷G来进行执行。
总结
go实现了CSP并发模型作为并发基础,底层使用goroutine作为并发实体,goroutine非常轻量级可以创建几十万个实体!实体间通过channel进行匿名消息传递进行解耦,在语言层面实现了自动调度,这样屏蔽了很多内部细节,对外提供简单的语法关键字,大大简化了并发编程的思维转换和管理线程的复杂度。
Channel类型
创建Channel
var c1 chan [value type]
c1 = make([channel type] [value type], [capacity])
- [value type] 定义的是 Channel 中所传输数据的类型
- [channel type] 定义的是 Channel 的类型,有三种类型:
chan
可读可写chan<-
仅可写<-chan
仅可读
- [capacity] 是一个可选参数,其定义的是 channel 中的缓存区。如果不填写则默认该 channel 没有缓存区。对于没有缓存区的 channel,消息的发送和收去必须能同时完成,否则会造成阻塞并提示死锁错误!
通过 Channel 发送和接收消息
示例代码:
package main
import "fmt"
func main(