入坑 go 也快一年了,从今天开始会定期分享一下 Go 语言学习过程中的一些基础知识。
go 语言中的管道, 主要是用于协程之间的通信, 比 UNIX 的管道更加轻量和易用。
我们先看一下管道的数据结构:
type hchan struct {
gcount uint // 环形队列剩余元素个数
dataqsiz uint // 环形队列长度
buf unsafe.Pointer // 环形队列指针
elemsize uint16 // 每个元素大小
closed uint32 // 标识关闭状态
elemtype *_type // 元素类型
sendx uint // 下一个元素写入时的下标
recvx uint // 下一个元素读取时的下标
recvq waitq // 等待读消息的队列
sendq waitq // 等待写消息的队列
lock mutex // 互斥锁, 保障管道无法并发读写
}
源码链接:
https://github.com/golang/go/blob/0d0193409492b96881be6407ad50123e3557fdfb/src/runtime/chan.go#L33
通过上述数据结构, 我们可以理解管道是由三部分组成的:
环形队列
读写等待队列
队列元素基本信息
从管道读取数据时, 如果管道缓冲区为空或者没有缓冲区, 那么当前协程就会阻塞, 然后放入 recvq 队列中。
往管道写入数据时, 如果管道缓冲区为空或者缓冲区满了, 那么当前协程就会阻塞, 然后放入 sendq 队列中。
读阻塞的协程会被新来的写数据的协程唤醒。
写阻塞的协程会被新来的读数据的协程唤醒。
同时上述数据结构中, 我们可以看到一个管道中只能传递一种元素类型。 如果想数据类型动态化, 可以传递 interface。
管道的操作:
初始化有两种方式:
变量声明:
var ch chan int // 声明一个新的管道
使用 make:
ch1 := make(chan string) // 无缓冲管道
ch1 := make(chan string 3) // 有缓冲管道
管道的读写是通过操作符: 「<-」控制的,管道在左边表示把右侧数据写入到管道中, 管道在右边表示读取管道数据赋值给左侧变量。
ch1 := make(chan string) // 初始化
ch1 <- \"gjl\"; // 把 gjl 字符串写入到管道中
c := <- ch1; // 读取管道数据并交给 c 变量
fmt.Println(c) // 输出
同时也可以通过操作符来限制管道的读写权限。
举个栗子🌰:
func ChanRW(ch chan int) { // 可以读写
}
func ChanR(ch <-chan int){ // 仅可以读
}
func ChanW(ch chan<- int){ // 仅可以写
}
注意:对于值为 nil 的管道, 无论读写都会使协程永久阻塞。
最后我们具体看一下管道发送和接收时的处理流程图, 用 processon 简单画一下图(最近沉迷练习画图 - -):
发送:
接收:
如果您在实际开发过程中遇到过 Python、数据库、爬虫、分布式、消息队列等方面的难题, 也欢迎在公众号或评论区留言, 我们一起探讨解决方案
如果本篇内容能够帮助到您, 希望您关注我的公众号: 「python 学习爱好者」, 希望与您一起共同成长~
来源:https://www.cnblogs.com/guanjinglin/p/16646496.html
本站部分图文来源于网络,如有侵权请联系删除。