持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
文章概览
- 管道的实现原理
- 管道的使用
一、管道实现原理
管道其实是一个struct结构体,里面包含队列、类型信息、协程等待队列等,管道用一个环形队列来实现缓冲区。协程的阻塞是通过等待队列实现的。
向管道中写入数据
- 如果缓冲区中有空余位置,则将数据写入缓冲区,结束发送过程。
- 如果缓冲区无空余位置,就会把当前协程存入sendq队列,当前携程进入睡眠并等待被唤醒。
由于管道的此特性,当接收队列存在携程时,说明缓冲区中没有数据,但有协程在等待读数据,因此可以直接把数据给等待中的读协程,不需要再写入缓冲区。
从管道读取数据
- 当缓冲区中有数据时,从缓冲区中读取数据,读取过程结束。
- 当缓冲区中没有数据时,将当前的读协程加入等待队列,并进入睡眠等待新数据的写入。
同样,当等待发送数据的队列不为空,且没有缓冲区,则读协程可以直接唤醒等待队列的第一个协程,读取它的数据。
关闭管道
关闭管道时,会把等待中的读协程全部唤醒,这些读协程只能读到管道类型的零值。同样,如果等待队列中有等待发送的写协程,同样会唤醒他们,但是会触发panic。
二、管道的使用
单向管道
单向管道只能用于发送或接收数据,但是管道的数据结构并没有体现出单向性。那么单向管道实际上是一种使用限制。
- func read(c <-chan int):通过形参限定函数内部只能从管道中读取数据。
- func send(c chan<- int):通过形参限定函数内部只能向管道中写入数据。
select
select可以监控多个管道,当其中一个管道可操作时就会触发相应的case分支,无论管道是否为空,case语句读取管道时不会阻塞,这时由于case语句编译后调用读管道时会明确传入不阻塞的参数,读不到数据时不会将当前协程加入等待队列,而是直接返回。