• [golang]channel源码



    简单概括:

    1个数据缓存+2个协程队列

    解释:
    数据缓存:是一个环形队列,存放具体数据,是int还是bool还是结构体

    协程队列包含发送写协程队列和读协程队列
    写协程队列,:当数据缓存满时,写操作会阻塞,并放入写协程队列
    读协程队列:当数据缓存空时,读操作会阻塞,并放入读协程队列.

    原理就是很简单,具体结构在 runtime.hchan中

    源码

    当我们创建 var c = make(chan boo,2l)时

    • qcount 代表当前队列剩余元素个数.    一个没插入,因此是0
    • dataqsiz 环形队列长度, 你设置的是2,那么他就是2. 如果是无缓冲的channel,就是0
    • bvuf 环形队列指针   指向具体的缓存对象
    • elemsize 每个元素大小,int型的就是8字节  ,bool型的就是1字节 
    • sendx 写入元素位置下标.   你插入一个
    • recvx 从队列的该位置读出 
    • recvq 等待读消息的协程队列 
    • sendq 等待写消息的协程队列 
    • lock 互斥锁,不允许并发操作

    写入流程:

    1.有缓存则放缓存

    2.缓存没位置则放sendq,等待唤醒

    读取流程

    1.sendq不为空,缓存不为空,先拿缓存的,再从sendq拿出一个数据,放到缓存,唤醒之前阻塞的g

    2.sendq不为空,缓存为空,说明缓存长度是0, 直接取出g,拿走,唤醒.

    3.sendq为空,缓存不为空,从缓存拿数据走人

    4.sendq为空,缓存为空,说明没人放东西,阻塞

    close流程

    1.已经关闭的,再次调用会panic

    2.唤醒sendq,触发panic(panic: send on closed channel),

      唤醒recvq,设置g的值为nil,用户读到的数据是类型的零值

    这里有个点,别混了,如果一个channel关闭了,再去读取,会返回零值么?不是的,会看缓存数据是否为空,不为空,则返回缓存数据内容,为空,则返回零值.   上面这句话,recvq中有协程队列,一个隐含意思就是,你的缓存中已经没有数据了.

    那么你可能会有疑问,你读取到了一个类型的零值,你怎么知道是close之后返回的零值,还是说就写入了一个零值?

    这个问题和map一样,如果你读取map的key,返回了一个零值,你怎么知道是本身存的零值还是没有key返回的零值呢? 就是加个bool返回值呗,多告诉你点信息.

    1. func TestOk(t *testing.T) {
    2. a := make(chan string, 2)
    3. a <- "a"
    4. close(a) // ① 关闭channel
    5. go func() {
    6. d, ok := <-a
    7. if ok {
    8. fmt.Println("read data: " + d)
    9. }
    10. d2, ok := <-a
    11. if !ok { // ②读取是否成功
    12. fmt.Println("not read data: " + d2)
    13. }
    14. }()
    15. time.Sleep(10 * time.Second)
    16. }
    17. 如果没有①的代码. 即不关闭.
    18. 输出结果:
    19. read data: a
    20. 如果有①的代码.
    21. read data: a
    22. not read data:

    其他:

    1.channel没有初始化,去读写.会阻塞

     2.channel关闭后去写,再次关闭,会引发panic.   

    panic: send on closed channe

    注意:读不会,读的时候,1.如果缓存有值,则返回缓存数据 2.缓存无值,会返回零值.

    3.读写channel时一定会阻塞么

    有个block的参数可以控制

    比如 select操作,编译时候就翻译成 not block了

    4.range读取channel

    规则:读取时,如果读不到,会一直阻塞.   当被close掉,会解除阻塞状态.

    其实都是语法糖,你普通读,读不到也是阻塞,当close掉,先读缓存,缓存为空返回零值. 

    只不过range,在close掉后,不会进入到range的业务代码里.直接跳过去.

    相当于:

    1. for true {
    2. data, ok := <-a
    3. if ok {
    4. fmt.Println(data)
    5. } else {
    6. break
    7. }
    8. }
  • 相关阅读:
    大语言模型的三阶段训练
    新增成功后跳转到表格列表刷新表格数据
    哈希冲突和一致性哈希
    blog--4美化主题
    Hadoop生态圈中的数据同步工具SQOOP
    1011 World Cup Betting
    杨辉三角c语言程序
    MFC Windows 程序设计[128]之复合控件模型
    Oracle GoldenGate实现数据同步(Windows环境下的内外网同步)
    【无标题】
  • 原文地址:https://blog.csdn.net/u012997470/article/details/126126711