Do not communicate by sharing memory; instead, share memory by communicating.
不要通过共享内存来通信,而要通过通信来实现内存共享。
CSP(Communicating Sequential Processes)模型
数据结构
1 |
|
1 |
|
向channel发送数据
发送操作: chansend()函数 ,func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool
如果检测到channel为nil,当前goroutine会被挂起
对于不阻塞的channel,快速检测失败场景:
- channel无缓冲区,且等待接收队列里没有goroutine
- channle有缓冲区,但循环数组已经满了
代码表示为:1
2
3
4if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
(c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
return false
}
上锁 // 为什么加在快速检测失败判断后面:少获取一次锁,提升性能。
如果检测到channel已经关闭,直接panic
如果能从等待队列recvq里出队一个sudog(代表一个goroutine)
如果channel closed,解锁,直接返回panic
如果接收队列里面有goroutine,直接将要发送的数据拷贝到接收goroutine, return true
1.对于缓冲型的gorouting,如果还有缓冲空间 ,发送到缓冲区区
2.qp = buf的sendx位置
3.将数据从ep拷贝到qp
4.发送游标值加1 c.sendx++,如果发送游标值等于缓冲空间容量,游标值归零,c.sendx=0
5.缓冲区的数量加一 ,c.qcount++
6.解锁,return true
如果不需要阻塞,解锁,并直接返回false
channel满了,发送方会被阻塞,接下来会构造一个sudog
获取当前goroutine的指针,当前goroutine进入等待队列,当前goroutine被挂起
channel接收数据
chanrecv()函数
1.如果 channel == nil , 如果不阻塞 !block ,直接返回;否则 接收一个nil的channel ,goroutine挂起
- 非阻塞模式下 快速检测失败:
- 非缓冲型: sendq里面没有goroutine在等待
- 缓冲型:但buf里面没有元素
如果channel 被closed,return
3.加锁 lock(&c.lock)
- 读取数据:
channel已关闭,且循环数组buf里没有元素 ,
nil channel :
closed panic
读: 阻塞
写:阻塞
closed channel:
close: panic
读:对应对象的零值
写:pinc
not nil,not closed channel :
close : 正常关闭
读: 阻塞或正常读取数据。缓冲型channel为空 or 非缓冲型channel无等待发送者goroutin
写:阻塞或正常写入。缓冲型channel buf 满了 or 非缓冲型channel无等待发送者channel
channle应用
停止信号: