在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
废话不多说,直奔主题。 简单说明:
源码位于
下面我们来详细介绍 我们首先创建一个channel。
创建channel实际上就是在内存中实例化了一个 先考虑一个问题,如果你想让goroutine以先进先出(FIFO)的方式进入一个结构体中,你会怎么操作? channel中有个缓存buf,是用来缓存数据的(假如实例化了带缓存的channel的话)队列。我们先来看看是如何实现“队列”的。
当使用 然后开始
二
三
这时候满了,队列塞不进去了 然后是取 然后开始
二
三
图为: 注意以上两幅图中 缓存中按链表顺序存放,取数据的时候按链表顺序读取,符合FIFO的原则。 注意:缓存链表中以上每一步的操作,都是需要加锁操作的! 每一步的操作的细节可以细化为:
每一步的操作总结为动态图为:(发送过程) 或者为:(接收过程) 所以不难看出,Go中那句经典的话: 使用的时候,我们都知道,当channel缓存满了,或者没有缓存的时候,我们继续send(ch <- xxx)或者recv(<- ch)会阻塞当前goroutine,但是,是如何实现的呢? 我们知道,Go的goroutine是用户态的线程( goroutine的阻塞操作,实际上是调用
这个时候G1正在正常运行,当再次进行send操作(ch<-1)的时候,会主动调用Go的调度器,让G1等待,并从让出M,让其他G去使用 同时G1也会被抽象成含有G1指针和send元素的 那么,G1什么时候被唤醒呢?这个时候G2隆重登场。 G2执行了recv操作 G2从缓存队列中取出数据,channel会将等待队列中的G1推出,将G1当时send的数据推到缓存中,然后调用Go的scheduler,唤醒G1,并把G1放到可运行的Goroutine队列中。 你可能会顺着以上的思路反推。首先: 这个时候G2会主动调用Go的调度器,让G2等待,并从让出M,让其他G去使用。 此时恰好有个goroutine G1开始向channel中推送数据 G1并没有锁住channel,然后将数据放到缓存中,而是直接把数据从G1直接copy到了G2的栈中。 之后的事情显而易见: 参考文献: |
请发表评论