channel执行顺序
这里我们先抛出结论:channel的执行是随机的。
想要证明这一点很简单,下面我们使用反证法来证明这一点,现在我们定义如下3个协程,同时再main协程上通过for循环不停的向chan中写数据,如果channel的执行是顺序的,那么下面额例子不会有任何输出;否则,证明channel是随机执行的。
例子:
package main
import (
"log"
)
func main() {
c := make(chan int)
go func() {
for {
value:=<-c
if value%3!=1{
log.Println("1:",false)
}
}
}()
go func() {
for {
value:=<-c
if value%3!=2{
log.Println("2:",false)
}
}
}()
go func() {
for {
value:=<-c
if value%3!=0{
log.Println("3:",false)
}
}
}()
index:=1
for {
c<-index
index++
}
}
之前的文章中,我们说明了,channel中recvq,sendq内部的实现是一个链表结构,既然是链表结构,那有一点可以明确,链表的访问必然是有序的,既然都是有序的那为什么会导致channel是随机的?
从源码看chansend()的实现
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
// 忽略一些安全验证的代码
...
//如果接收队列不为空,则调用send函数
if sg := c.recvq.dequeue(); sg != nil {
// Found a waiting receiver. We pass the value we want to send
// directly to the receiver, bypassing the channel buffer (if any).
send(c, sg, ep, func() { unlock(&c.lock) }, 3)
return true
}
// 如果chann还有空间,则做一些修改hchan状态的操作
if c.qcount < c.dataqsiz {
...
}
...
// 如果上面的代码不能执行,则说明还没有接收者,则需要创建接收者
gp := getg()
// 下面创建一个sudog,sudog可以理解为协程状态
mysg := acquireSudog()
mysg.releasetime = 0
if t0 != 0 {
mysg.releasetime = -1
}
// 这个协程关联一个值为ep,ep就是我们使用<-传递的值
mysg.elem = ep
mysg.waitlink = nil
// 将协程状态关联到一个协程
mysg.g = gp
mysg.isSelect = false
// 协程状态关联chan,协程执行时,我们需要通过hchan对象获取一些数据
mysg.c = c
gp.waiting = mysg
gp.param = nil
// 将我们刚创建的协程状态,放入sendq队列中
c.sendq.enqueue(mysg)
gp.waiting = nil
gp.activeStackChans = false
closed := !mysg.success
gp.param = nil
mysg.c = nil
// 释放我们创建临时变量占用的内存
releaseSudog(mysg)
...
return true
}
上面这段源码主要完成了,以下几件事:
- 如果存在接收队列,则直接将chan收到的数据通过send函数发送出去
- 如果不存在接收队列,则先将chan收到的数据保存在hchan.buf上并修改hchan对象相应的值
- 创建一个sudog对象,该对象记录了它将要在哪个协程上执行,并记录执行需要的一些状态数据,比如当前关联的chan的值,以及chan对象本身等
- 在创建好sudog对象后,因为此时不存在接收者,所以要进行缓存,此时就将sudog对象放入当前hchan对象对应的sendq队列中
- 清理sudog对象占用的空间,归还内存
从源码从,我们可以看出,chansend虽然是顺序被调用的,但是由于chansend内部的实现是通过协程来完成,如果没有接收者时,需要缓存一些这些值,以便后续协程执行时使用,我们知道协程时无序的,那么chan也无法保证写入的值是顺序被接收者接收的。
但是,当存在接收者时会调用send方法,这将导致数据没有被保存在buf中,那么这是不是说明:如果存在接收者时,channel就是有序的呢?