go中的channel类型可以在goroutine之间发送和接收消息。
传统的并发编程模式——多线程编程,大多时候是基于共享内存的方式来完成的。传统多线程的并发模式使用locks锁、condition variable条件变量 等同步原语或硬件指令来强制规定进程的推进顺序。
除了直接控制线程,还有CSP和Actor模型是基于消息传递。
这里我只简单介绍一下CSP。
CSP——Communicating Sequential Processes
CSP的核心思想是多个线程之间通过Channel来通信,类似于操作系统中的Pipe。CSP中channel是第一类对象,它不关注发送消息的实体。
go的channel正是借鉴了CSP这种模型。
这里再多说一些,
goroutine底层是使用协程coroutine实现并发,coroutine是一种运行在用户态的用户线程,有以下特点:
不过学过操作系统的同学应该都知道,这样的并发最终还是会涉及到内核空间的。只是用户线程和内核线程的调度方案可以是一对一、多对一、多对多,使用多对多在这里可以达到提高效率的效果。
ch1 := make(chan int) //创建一个整型类型的通道
ch2 := make(chan interface{
}) //可以存放任意类型
ch1 <- 0
data := <-ch1
//执行该语句时将会阻塞,直到接收到数据并赋值给data变量
ch := make(chan int)
var ch1 chan<- int = ch //只能发送
ch2 := make(<-chan int) //只能读
//创建一个3个元素缓冲大小的整型通道
ch := make(chan int, 3)
channel分为有缓冲和无缓冲的,无缓冲的只有当发送方和接收方都准备好才会传送数据,否则会被阻塞。在一个goroutine内同时进行读取和发送的话会导致死锁。
有缓冲的channel只有当缓冲区被装满了之后才会阻塞发送者,只有当缓冲区为空时才会阻塞接收者。
可以使用close关键字关闭一个channel,但是原则上由发送者关闭,如果仍然向一个已关闭的channel发送数据,会导致panic。关闭channel之后,接收方仍然可以从channel中读出数据默认值0,但是状态已经变了。
Go语言中的select 和操作系统中的系统调用select比较相似。
C语言的select系统调用可以同时监听多个文件描述符的可读或者可写的状态,Go 语言的select可以让Goroutine同时等待多个Channel可读或可写,在多个文件或Channel状态改变之前,select会一直阻塞当前线程或Goroutine。
select是与switch相似的控制结构,不过select的case中的表达式必须都是channel的收发操作。当select中的多个case同时被触发时,会随机执行其中一个。
通常情况下,select语言会阻塞goroutine并等待多个Channel中的一个达到可以收发的状态。但如果有default语句,可以实现非阻塞,就是当多个channel都不能执行的时候,运行default。
同时处理接收和发送多个通道的数据。
Go 语言中提供了select关键字,可以响应多个通道的操作。
select的每个case都会对应一个通道的收发过程。
当收发完成时,就会触发case中响应的语句。
select{
case 操作1:
响应操作1
case 操作2:
……
default:
……
}
type hchan struct {
qcount uint // 通道中的元素个数
dataqsiz uint // 通道中循环队列的长度
buf unsafe.Pointer //缓冲链表的指针,缓冲链表是一个循环链表
elemsize uint16 // 能接收发的元素大小
closed uint32
elemtype *_type // 能接收发的元素类型
//sendx和recvx用于记录buf这个循环链表中的发送或者接收的index
sendx uint // 发送操作处理到的位置
recvx uint // 接收操作处理到的位置
//分别是接收或者发送的goroutine抽象出来的结构体(sudog)的队列,
//都是双向链表
recvq waitq // 接收队列
sendq waitq // 发送队列
lock mutex
}
创建channel实际上就是在内存中实例化了一个hchan的结构体,并返回一个ch指针。使用过程中channel在函数之间的传递都是用的这个指针。
通道使用互斥锁mutex,让goroutine以先进先出FIFO的方式进入一个结构体中。
当需要发送或者接受的时候,要锁住hchan这个结构体。
缓存中按链表顺序存放,取数据按链表顺序读取。
当通道满了,再继续往通道发送会阻塞当前goroutine,原理是通过Go运行时的scheduler来完成调度。
https://www.jianshu.com/p/36e246c6153d
https://blog.csdn.net/sixdaycoder/article/details/90751972
https://www.cnblogs.com/lianggx6/p/12558663.html
https://blog.csdn.net/guyan0319/article/details/90201405
《Go语言高级编程》