Go管道

121 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

文章概览

  • 管道的实现原理
  • 管道的使用

一、管道实现原理

 管道其实是一个struct结构体,里面包含队列、类型信息、协程等待队列等,管道用一个环形队列来实现缓冲区。协程的阻塞是通过等待队列实现的。

向管道中写入数据

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

 由于管道的此特性,当接收队列存在携程时,说明缓冲区中没有数据,但有协程在等待读数据,因此可以直接把数据给等待中的读协程,不需要再写入缓冲区。

从管道读取数据

  • 当缓冲区中有数据时,从缓冲区中读取数据,读取过程结束。
  • 当缓冲区中没有数据时,将当前的读协程加入等待队列,并进入睡眠等待新数据的写入。

 同样,当等待发送数据的队列不为空,且没有缓冲区,则读协程可以直接唤醒等待队列的第一个协程,读取它的数据。

关闭管道

 关闭管道时,会把等待中的读协程全部唤醒,这些读协程只能读到管道类型的零值。同样,如果等待队列中有等待发送的写协程,同样会唤醒他们,但是会触发panic。

二、管道的使用

单向管道

 单向管道只能用于发送或接收数据,但是管道的数据结构并没有体现出单向性。那么单向管道实际上是一种使用限制。

  • func read(c <-chan int):通过形参限定函数内部只能从管道中读取数据。
  • func send(c chan<- int):通过形参限定函数内部只能向管道中写入数据。

select

 select可以监控多个管道,当其中一个管道可操作时就会触发相应的case分支,无论管道是否为空,case语句读取管道时不会阻塞,这时由于case语句编译后调用读管道时会明确传入不阻塞的参数,读不到数据时不会将当前协程加入等待队列,而是直接返回。