GO语言入门指南-chan(二) | 青训营

81 阅读2分钟

这是一篇后端实践选题,分析GOLANG的通道特点

GO语言入门指南-chan | 青训营 中讲了通道的用法和分类,这篇文章补充一下select的用法,以及通道的一些底层原理。

select

select有点类似switch,但只能配合通道使用。

在没有执行某条case之前,select会一直检查是否有通道收到消息,如果同时有多个case满足,则随机执行一条。

使用代码如下所示:

select {
case <- ch1:
    fmt.Println("case 1")
case <- ch2:
    fmt.Println("case 2")
default:
    fmt.Println("default")
}

当然,和switch类似,select也可以设置default,当没有case满足时,直接执行default结束select。

chan底层结构

在golang的src/runtime/chan.go中有通道的源码,在这里可以看到chan结构体的组成

type hchan struct {
    qcount   uint
    dataqsiz uint
    buf      unsafe.Pointer
    elemsize uint16
    closed   uint32
    elemtype *_type
    sendx    uint
    recvx    uint
    recvq    waitq
    sendq    waitq
    
    lock     mutex
}

简单来说,chan就是一个带锁的环形队列。

lock用于确保chan可以处理并发场景

qcount记录环形队列中的数据数量

dataqsiz记录数据大小

buf是指向真正存储数据的数组的指针

elemsize记录通道元素的大小

closed记录通道是否关闭

elemtype记录通道元素的类型

sendxrecvx分别记录环形队列中的入队索引和出队索引

recvqsendq分别记录写通道阻塞的goruntine和读通道阻塞的goruntine

在读写操作通道阻塞时,goruntine会被打包成sudog结构体放到这两个队列中,等待通道唤醒。

可以看到,recvqsendq的数据类型是waitq,这个结构体的结构如下:

type waitq struct {
   first *sudog
   last  *sudog
}

其实就是一个存储sudog的队列,存放了队列的队首和队尾指针,sudog中不仅包含了goruntine的信息,还有指向前后sudog的指针,构成一个双向链表。


以上只是看源码过程中的一些简单理解,如果有不对的地方,欢迎批评指正!