开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
并发是指逻辑上具备同时处理多个任务的能力;并行则是物理上同时执行多个任务。
大多数的编程语言的并发编程模型是基于线程和内存同步访问控制,Go 的并发编程的模型则用 goroutine 和 channel 来替代。Goroutine 和线程类似,channel 和 mutex (用于内存同步访问控制)类似。
1. Channel设计的目标
channel主要用来设计进行多任务间的数据传递,是线程安全的。Channel中发送数据到Channel和从Channel接受一个数据都是原子性
使用场景:goroutine之间的消息传递,并发控制,事件订阅和广播
2. Channel的底层实现
channel是golang中用来实现多个goroutine通信的管道,它的底层是一个叫做hchan的结构体。在go的runtime包下。
channel 的底层就是通过 mutex 来控制并发的。只是 channel 是更高一层次的并发编程原语,封装了更多的功能。
2.1数据结构
type hchan struct {
qcount uint // 循环数组中的元素数量
dataqsiz uint // 循环数组的长度
buf unsafe.Pointer // 指向底层循环数组的指针
elemsize uint16 // chan 中元素大小
closed uint32 // chan 是否被关闭的标志
elemtype *_type // channel中的元素类型
sendx uint // 下一次发送数据的下标位置
recvx uint // 下一次读取数据的下标位置
recvq waitq // 读等待队列
sendq waitq // 写等待队列
lock mutex //互斥锁,保证读写channel时不存在并发竞争问题
}
Channel的结构体主要组成部分:
-
buf:用来保存goroutine之间传递数据的循环链表,只有缓冲型的channel才有
-
sendx和recvx:用来记录此循环链表当前发送或者接收数据的下标值
-
sendq和recvq:保存被阻塞的goroutine
- 当循环数组中没有数据时,收到了接收请求,那么接收数据的变量地址将会写入读等待队列
- 当循环数组中数据已满时,收到了发送请求,那么发送数据的变量地址将写入写等待队列
-
lock:保证channel写入和读取数据时线程安全的锁
无缓冲和有缓冲区别: 管道没有缓冲区,从管道读数据会阻塞,直到有协程向管道中写入数据。同样,向管道写入数据也会阻塞,直到有协程从管道读取数据。管道有缓冲区但缓冲区没有数据,从管道读取数据也会阻塞,直到协程写入数据,如果管道满了,写数据也会阻塞,直到协程从缓冲区读取数据。
3. channel的相关操作
口诀:空读写阻塞,写关闭异常,读关闭空零
| 操作 | nil channel | closed channel | not nil, not closed channel |
|---|---|---|---|
| close | panic | panic | 正常关闭 |
| 读 <- ch | 阻塞 | 读到对应类型的零值 | 阻塞或正常读取数据。缓冲型 channel 为空或非缓冲型 channel 没有等待发送者时会阻塞 |
| 写 ch <- | 阻塞 | panic | 阻塞或正常写入数据。非缓冲型 channel 没有等待接收者或缓冲型 channel buf 满时会被阻塞 |
发生 panic 的情况有三种:向一个关闭的 channel 进行写操作;关闭一个 nil 的 channel;重复关闭一个 channel。读、写一个 nil channel 都会被阻塞。
3.1 写数据
向 channel 写数据的流程: 如果等待接收队列 recvq 不为空,说明缓冲区中没有数据或者没有缓冲区,此时直接从 recvq 取出 G,并把数据写入,最后把该 G 唤醒,结束发送过程; 如果缓冲区中有空余位置,将数据写入缓冲区,结束发送过程; 如果缓冲区中没有空余位置,将待发送数据写入 G,将当前 G 加入 sendq,进入睡眠,等待被读 goroutine 唤醒;
3.2 读数据
向 channel 读数据的流程: 如果等待发送队列 sendq 不为空,且没有缓冲区,直接从 sendq 中取出 G,把 G 中数据读出,最后把 G 唤醒,结束读取过程; 如果等待发送队列 sendq 不为空,此时说明缓冲区已满,从缓冲区中首部读出数据,把 G 中数据写入缓冲区尾部,把 G 唤醒,结束读取过程; 如果缓冲区中有数据,则从缓冲区取出数据,结束读取过程;将当前 goroutine 加入 recvq,进入睡眠,等待被写 goroutine 唤醒;