美文网首页
go channel 底层实现原理

go channel 底层实现原理

作者: Newzer | 来源:发表于2023-07-23 14:53 被阅读0次

    首先看一下它的结构体

    type hchan struct {
        qcount   uint           // total data in the queue,队列长度
        dataqsiz uint           // size of the circular queue,队列容量
        buf      unsafe.Pointer // points to an array of dataqsiz elements,队列底层数组指针
        elemsize uint16 //元素类型大小
        closed   uint32 //是否关闭 
        elemtype *_type // element type,元素类型
        sendx    uint   // send index,发送索引
        recvx    uint   // receive index,接收索引
        recvq    waitq  // list of recv waiters,接收goroutine链表
        sendq    waitq  // list of send waiters,发送goroutine链表
    
        // lock protects all fields in hchan, as well as several
        // fields in sudogs blocked on this channel.
        //
        // Do not change another G's status while holding this lock
        // (in particular, do not ready a G), as this can deadlock
        // with stack shrinking.
        lock mutex //锁
    }
    

    1.有lock字段就说明channe是线程安全的
    2.recvq,sendq都是goroutine链表的封装,recvq表示这个channel的多个接收goroutine,sendq表示这个channel的多个发送goroutine,在发送数据或者关闭channel的时候会用到这两个字段
    3.qcount,dataqsiz,buf,sendx,recvx 这几个字段都是和缓冲channel有关的,sendx,recvx会在发送数据和接收数据的时候会用到这两个字段

    发送数据原理:
    1.发送数据
    2.轮训检查recvq链表中是否有接收的goroutine,如果有,直接就把这个发送的值拷贝到接收goroutine的栈里直接返回,如果没有进行下一步
    3.如果是一个带缓冲区的channel,检查容量是否已满,不满则根据sendx插入到缓冲数组中,直接返回,已满则该协程阻塞

    接收数据原理:
    1.检查channel是否已经关闭,关闭了就直接跳到第3步,没关闭就第2步
    2.轮训检查sendq链表中是否有发送的goroutine,如果有,直接就把这个发送的值拷贝到接收goroutine的栈里直接返回,如果没有进行下一步
    2.如果是一个带缓冲区的channel,检查循环数组中是否有可以发送的元素,有则根据recvx将元素拷贝到该goroutine栈中,直接返回,没有可放出的元素则该协程阻塞

    所以仍然可以从一个已经关闭的带有缓冲区的channel读取出数据
    如果一个channel被多个goroutine监听,则数据会随机被某个goroutine消费

    总的来说,channe 协程间通信的本质是拷贝,优先是recvq,sendq与当前协程之间直接的数据拷贝,若存在缓冲区,则还有缓冲区和协程之间的拷贝

    关闭channel时发生了什么?
    先将closed赋值为1
    加一把大锁【遍历所有的recvq,sendq加入到一个全部协程池】解锁,唤醒全部的协程,所有sendq协程直接panic,所有recvq返回默认值

    相关文章

      网友评论

          本文标题:go channel 底层实现原理

          本文链接:https://www.haomeiwen.com/subject/tthzudtx.html