Go 原理剖析(数据结构)- chan

chan 底层是一个环形缓冲数组

type hchan struct {
    qcount   uint    // 元素数量
    dataqsiz uint    // 容量
    buf      unsafe.Pointer    // 指向环形缓冲数组的指针
    elemsize uint16
    closed   uint32    // 是否关闭
    elemtype *_type    // chan 中元素的类型
    sendx    uint    // 下次写的索引
    recvx    uint    // 下次读的索引
    recvq    waitq    // 读等待队列(chan 为空时,阻塞读 chan 的 goroutine)
    sendq    waitq    // 写等待队列(chan 已满时,阻塞写 chan 的 goroutine)

    lock mutex    // chan 的互斥锁
}

type waitq struct {
    first *sudog    // 队头指针
    last  *sudog    // 队尾指针
}

type sudog struct {
    g *g    // 绑定的 goroutine

    next *sudog
    prev *sudog

    // 等待写入 chan 的数据(写等待 goroutine)
    // 等待从 chan 中读出的数据(读等待 goroutine)
    elem unsafe.Pointer

    isSelect bool    // 是否因 select 而被阻塞

    // true:因 chan 通信而被唤醒
    // false:因 chan 关闭而被唤醒
    success bool

    c *hchan    // 绑定的 chan
}

写 chan

chan 为 nil

当前 goroutine 被永久性阻塞(如果是 main goroutine,fatal error 程序退出)

chan 已关闭

panic

读等待队列非空

  1. 拿锁
  2. 从读等待队列 recvq 中取出队头的 sudog
  3. 将数据直接写入 sudog.elem
  4. 释放锁
  5. 唤醒 sudog 对应的读等待 goroutine

读等待队列为空,buf 未满

  1. 拿锁
  2. 将数据写入 sendx 处
  3. sendx++,qcount++
  4. 释放锁

读等待队列为空,buf 已满

  1. 获取一个 sudog,绑定对应的 goroutine 和 chan
  2. 将 sudog 放入写等待队列 sendq
  3. 挂起当前 goroutine

读 chan

chan 为 nil

当前 goroutine 被永久性阻塞(如果是 main goroutine,fatal error 程序退出)

chan 已关闭

可以继续读取,若 buf 为空,会得到 chan 对应类型的零值

写等待队列非空

  1. 拿锁
  2. 从写等待队列 sendq 中取出队头的 sudog
  3. 如果环形缓冲数组容量为 0,直接读取 sudog.elem 中的数据;否则,说明此时环形缓冲数组已满,读取 recvx 处的数据,并将 sudog.elem 中的数据直接写入 recvx 处,recvx++,sendx = recvx
  4. 释放锁
  5. 唤醒 sudog 对应的写等待 goroutine

写等待队列为空,buf 非空

  1. 拿锁
  2. 读取 recvx 处的数据
  3. recvx++,qcount–
  4. 释放锁

写等待队列为空,buf 为空

  1. 获取一个 sudog,绑定对应的 goroutine 和 chan
  2. 将 sudog 放入读等待队列 recvq
  3. 挂起当前 goroutine

关闭 chan

  1. chan 为 nil 或 chan 已关闭 -> panic
  2. 关闭 chan
  3. 唤醒所有被该 chan 阻塞的 goroutine

select

  • 非阻塞型(with default)
  • 阻塞型(without default)

随机执行一个 case,如果所有的 case 都无法完成,有 default 则走 default,没有 default,则将当前 goroutine 加入到所有 case 对应 chan 的等待队列中,并挂起当前 goroutine

如果当前 goroutine 被某个 case 上的 chan 唤醒,还要将当前 goroutine 从所有 case 对应 chan 的等待队列中移除

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇