// 位于 src/runtime/chan.go
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
// 不能阻塞,直接返回 false,表示未发送成功
gopark(nil, nil, "chan send (nil chan)", traceEvGoStop, 2)
// 如果 channel 未关闭且 channel 没有多余的缓冲空间。这可能是:
// 1. channel 是非缓冲型的,且等待接收队列里没有 goroutine
// 2. channel 是缓冲型的,但循环数组已经装满了元素
if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
(c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
if blockprofilerate > 0 {
panic(plainError("send on closed channel"))
// 如果接收队列里有 goroutine,直接将要发送的数据拷贝到接收 goroutine
if sg := c.recvq.dequeue(); sg != nil {
send(c, sg, ep, func() { unlock(&c.lock) }, 3)
// 对于缓冲型的 channel,如果还有缓冲空间
if c.qcount < c.dataqsiz {
qp := chanbuf(c, c.sendx)
typedmemmove(c.elemtype, qp, ep)
if c.sendx == c.dataqsiz {
// channel 满了,发送方会被阻塞。接下来会构造一个 sudog
goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3)
// 从这里开始被唤醒了(channel 有机会可以发送了)
throw("G waiting list is corrupted")
throw("chansend: spurious wakeup")
// 被唤醒后,channel 关闭了。坑爹啊,panic
panic(plainError("send on closed channel"))
if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2)