祗疼妳一个 发表于 2024-1-22 15:19:45

go channel

模型

https://img2023.cnblogs.com/blog/595074/202312/595074-20231201221342096-710784674.png三部分组成:

发送等待队列
接收等待队列
管道 缓存区定义

在 runtime 的chan.go中
type hchan struct {
        qcount   uint         
        dataqsiz uint         
        buf      unsafe.Pointer
        elemsize uint16
        elemtype *_type // element type
    // 上面的这几个组成了一个环形缓存区

        closed   uint32 // 关闭状态

    // 下面4个组成了2个队列 g的
        sendx    uint   // send index
        recvx    uint   // receive index
        recvq    waitq// list of recv waiters
        sendq    waitq// list of send waiters

        // lock protects all fields in hchan, as well as several
        lock mutex// 锁,用于在操作 channel的 元素时候,保证并发安全
}特殊缓存区

https://img2023.cnblogs.com/blog/595074/202312/595074-20231201222419529-704199355.pngqcount已经存储的个数
dataqsiz环形队列缓存的容量,即允许缓存的消息最大个数
buf   指向这个缓存区的地址
elemsize 元素的大小
elemtype元素的类型设计成这样,主要目的不需要gc来清理,因为环形机构,会自动把删除数据内存给占了。
环形缓存可以大幅降低GC的开销
两个队列

https://img2023.cnblogs.com/blog/595074/202312/595074-20231201223331538-870813795.png这里还要看下 waitq 的定义
type waitq struct {
        first *sudog
        last*sudog
}

// 在sema中也有用到,就g结构体的封装
type sudog struct {
      g *g
        next *sudog
        prev *sudog
        elem unsafe.Pointer // 接受参数的地址
    // 当是接收g时候,如果有缓存中有数据,直接把数据拷贝到这个地址}
注意:这里的sendx 并不是指向发送队列中的g,而且发送队列应该写入环形缓存区的index,
同理,recvx也是,指向接受数据的g,应该从缓冲区的那个index取数据
互斥锁

lock mutex互斥锁并不是排队发送/接收数据
不是让发送和接收队列来排队的,这些发送和接收数据的队列,休眠也不是在锁的sema里
互斥锁保护的hchan结构体本身
所以, Channel并不是无锁的
状态值

        closed   uint32 // 关闭状态0为开启、1为关闭
当一个关闭的channel,再往里写或者重复关闭、就会panic。但是可以读。发送数据

c
页: [1]
查看完整版本: go channel