美文网首页
go 的 channel 实现原理

go 的 channel 实现原理

作者: wayyyy | 来源:发表于2022-08-30 01:07 被阅读0次
    数据结构

    src/runtime/chan.go:hchan 中定义了管道的数据结构:

    type hchan struct {
        qcount   uint           // chan 里元素数量
        dataqsiz uint           // 底层循环数组的数量
        buf      unsafe.Pointer // 指向底层循环数组的指针,只针对有缓冲的 channel
        elemsize uint16         // chan 中元素大小
        closed   uint32         // chan 是否被关闭的标志
        elemtype *_type         // chan 中元素类型
        sendx    uint           // 已发送元素在循环数组中的索引
        recvx    uint           // 已接收元素在循环数组中的索引
        recvq    waitq          // 等待接收的 goroutine 队列
        sendq    waitq          // 等待发送的 goroutine 队列
    
        lock mutex              // 保护 hchan 中所有字段
    }
    

    从上面的数据结构可以看出来,hchan 由 缓冲队列,类型信息,协程等待队列 和 互斥锁 组成。

    • 环形队列
      chan 内部实现了一个环形队列作为其缓冲区,队列的长度是在创建chan时指定的。
      image.png
    • 等待队列

      • 从 channel 读取数据时,如果 channel 为空或者没有缓冲区,则当前协程会被阻塞,并被加入recvq 队列。
      • 从 channel 写入数据时,如果 channel 已满或没有缓冲区,则当前 协程 会被阻塞,并被加入到 sendq 队列
      image.png

      处于等待队列中的协程会在其他协程操作channel 时被唤醒:

      • 因读阻塞的协程会被向channel 写入数据的协程唤醒
      • 因写阻塞的协程会被从channel 读取数据的协程唤醒
    • 类型信息
      一个 channel 只能传递一种类型的值,类型信息存储在 hchan 数据结构中。

      • elemtype 代表类型,用于在数据传递过程中赋值
      • elemsize 代表类型大小,用于在buf中定位元素的位置
    • 互斥锁
      一个管道同时仅允许被一个协程读写,所以这里还会包含一个互斥锁。

    channel 的 创建

    创建 channel 的过程实际上是初始化 hchan 结构体的过程,其中类型信息和缓冲区的产固定由内置函数make指定,buf 的大小则由元素大小和缓冲区长度共同决定。

    向 channel 写入数据

    关闭 channel 会把 recvq 中的协程全部唤醒,这些协程获取的数据都为对应类型的零值,同时会把 sendq 队列中的协程全部唤醒,但这些协程会触发 panic

    向一个 channel 写入数据的过程如下:

    • 如果缓冲区有空余位置,则将数据写入缓冲区,结束发送过程
    • 如果缓冲区没有空余位置,则将当前协程加入sendq队列,进入睡眠并等待被读协程唤醒。

    这里实现上还有个小技巧,当接收队列recvq不为空时,说明缓冲区没有数据但有协程在等待数据,此时会把数据直接传递给recvq的队列中的第一个协程,而不必再写入缓冲区。

    image.png
    向 channel 读取数据

    如果一个 channel 读取数据简单过程如下:

    • 如果缓冲区有数据,则从缓冲区取出数据,结束读取过程。
    • 如果缓冲区中没有数据,则将当前协程中加入recvq队列,进入睡眠并等待被写协程唤醒。

    类似的,如果等待发送队列sendq不为空,且没有缓冲区,那么此时将直接从 sendq 队列第一个协程中获取数据。

    image.png
    关闭 channel

    关闭 channel 时会把 recvq 中的协程全部唤醒,这些协程获取的数据都为对应类型的零值,同时会把 sendq 队列中的协程全部唤醒,但这些协程会触发 panic。

    相关文章

      网友评论

          本文标题:go 的 channel 实现原理

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