Channel
channel的作用
channel主要用于goroutine之间通讯和同步
设计思路是:不要通过共享内存来通讯,而是通过通讯来共享内存(前者就是传统的加锁,后者就是channel)
channel的底层数据结构
type hchan struct {//channel分为无缓冲和有缓冲两种。//对于有缓冲的channel存储数据,借助的是如下循环数组的结构qcount uint // 循环数组中的元素数量dataqsiz uint // 循环数组的长度buf unsafe.Pointer // 指向底层循环数组的指针elemsize uint16 //能够收发元素的大小closed uint32 //channel是否关闭的标志elemtype *_type //channel中的元素类型//有缓冲channel内的缓冲数组会被作为一个“环型”来使用。//当下标超过数组容量后会回到第一个位置,所以需要有两个字段记录当前读和写的下标位置sendx uint // 下一次发送数据的下标位置recvx uint // 下一次读取数据的下标位置//当循环数组中没有数据时,收到了接收请求,那么接收数据的变量地址将会写入读等待队列//当循环数组中数据已满时,收到了发送请求,那么发送数据的变量地址将写入写等待队列recvq waitq // 读等待队列sendq waitq // 写等待队列lock mutex //互斥锁,保证读写channel时不存在并发竞争问题
}
总结hchan结构体的主要组成部分有四个:
- 用来保存goroutine之间传递数据的循环链表。=====> buf。
- 用来记录此循环链表当前发送或接收数据的下标值。=====> sendx和recvx。
- 用于保存向该chan发送和从改chan接收数据的goroutine的队列。=====> sendq 和 recvq
- 保证channel写入和读取数据时线程安全的锁。 =====> lock
未缓冲区channel就是数组长度为1的channel
缓存区channel就是数组长度大于1的channel
select 用法
go 的 select 为 golang 提供了多路 IO 复用机制,和其他 IO 复用一样,用于检测是否有读写事件是否 ready
- 仅支持channel,且单case:要么读,要么写
- 每个case的执行顺序是随机的<无序>
- 存在default,则select不会被阻塞。有一个case可以执行,也不会阻塞
Golang channel语句什么时候会被阻塞呢?
- 向未缓冲的channel发送数据(ch <- value)
- 从未缓冲的channel接收数据(value := <-ch)
- 向已满的缓冲channel发送数据
- 从空的缓冲channel接收数据
- select没有default时
思考
channel锁力度是多少?读写能并发吗?
由数据结构可以看出来,锁的力度是整个通道,读写实际是串形执行的
读写等待队列的作用是啥?是什么数据结构?
读写等待队列会在缓存区满的时候使用。 属于链式结构,存放的是goroutine,主要作用是保证gotoutine执行的顺序性.
缓存区满的时候明明会阻塞,那等待队列不是用一个元素就够了么?为啥是个链表?
因为可能会阻塞多个goroutine。这个时候需要保证被阻塞goroutine的顺序性
对一个已经关闭的channel读写元素会怎么样?
写操作会panic,读操作会立即返回(剩余数据/或者零值)