本篇笔记是笔者在第六届字节跳动青训营期间学习 Go 语言的语法笔记,主要内容为管道
管道 channel
1. 概念
· 管道为引用类型,笔者将管道理解为使用数组实现的队列结构。
· 类似于切片,管道的相关属性有:address, len, cap。其中 address 指其引用到的实际地址空间,len 为当前管道中的元素,cap 为管道的容量
· 管道容量不可以动态增长
· 管道支持同时写和读
· 管道可以关闭,当管道关闭后,此管道不能够写(若写入会报错),但仍然可以读,当读完已关闭的管道中的所有内容后,管道会给读取者读完的信号(见下面语法),以供读取者继续操作
2.语法
· 定义管道
var stringChan chan string //declare a channel to store string
stringChan = make(chan string, 10) // allocate for this channel with 10 as its capacity
intChan := make(chan int) // declare and allocate, the default capacity is 0
· 关闭管道
close(intChan)
· 写读管道
stringChan <- "hello" // write "hello" to stringChan
stringChan <- "world"
getStr := <-stringChan // get string from stringChan as str: hello
getStr2, ok := <-stringChan // when stringChan is closed and stringChan is empty, ok will be value as false
· 循环访问管道
var stringChan chan string
stringChan = make(chan string, 10)
stringChan <- "hello"
stringChan <- "world"
close(stringChan) // if channel is not closed, there will be a fatal error: all goroutines are asleep - deadlock!
for v := range stringChan {
fmt.Println(v)
}
输出如下:
hello
world
Process finished with the exit code 0
3.注意事项
· 不能使用常规 for 循环访问管道,原因是 len() 值在从管道中取出值后会变化,但可以先将 len() 值保存下来再进行遍历:
n := len(intChan)
// right for
for i := 0; i < n; i++ {
tmp := <-intChan
fmt.Println(tmp)
}
// wrong for
for i := 0; i < len(intChan); i++ {
tmp := <-intChan
fmt.Println(tmp)
}
· 管道不允许动态扩容,管道满了之后还进行添加会报错,同时在空的管道中取值也会报错
4.使用案例
下面的案例实现了写读同时进行,当写协程写完数据后通过关闭数据管道的方式告知读协程,将读协程将管道中所有数据读取完且数据管道已关闭之后,将关闭控制管道,从而告知主函数读写操作完成,可以继续执行(案例中为退出程序)
package main
import "fmt"
func writeData(intChan chan int, n int) {
for i := 0; i < n; i++ {
value := i * 2
intChan <- value
fmt.Println("writeData: ", value)
}
close(intChan)
}
func readData(intChan chan int, control chan bool) {
for v := range intChan {
fmt.Println("readData: ", v)
}
close(control)
}
func main() {
n := 50
intChan := make(chan int, n)
controlChan := make(chan bool)
go readData(intChan, controlChan)
go writeData(intChan, n)
for {
_, ok := <-controlChan
if !ok {
break
}
}
// other option
//for _ = range controlChan {
//
//}
}
输出如下:
writeData: 0
writeData: 2
writeData: 4
writeData: 6
writeData: 8
writeData: 10
writeData: 12
writeData: 14
writeData: 16
writeData: 18
writeData: 20
writeData: 22
writeData: 24
writeData: 26
writeData: 28
writeData: 30
writeData: 32
writeData: 34
writeData: 36
writeData: 38
writeData: 40
writeData: 42
writeData: 44
writeData: 46
writeData: 48
writeData: 50
writeData: 52
writeData: 54
writeData: 56
writeData: 58
writeData: 60
writeData: 62
writeData: 64
writeData: 66
writeData: 68
writeData: 70
writeData: 72
writeData: 74
writeData: 76
writeData: 78
writeData: 80
writeData: 82
writeData: 84
writeData: 86
writeData: 88
writeData: 90
writeData: 92
writeData: 94
writeData: 96
writeData: 98
readData: 0
readData: 2
readData: 4
readData: 6
readData: 8
readData: 10
readData: 12
readData: 14
readData: 16
readData: 18
readData: 20
readData: 22
readData: 24
readData: 26
readData: 28
readData: 30
readData: 32
readData: 34
readData: 36
readData: 38
readData: 40
readData: 42
readData: 44
readData: 46
readData: 48
readData: 50
readData: 52
readData: 54
readData: 56
readData: 58
readData: 60
readData: 62
readData: 64
readData: 66
readData: 68
readData: 70
readData: 72
readData: 74
readData: 76
readData: 78
readData: 80
readData: 82
readData: 84
readData: 86
readData: 88
readData: 90
readData: 92
readData: 94
readData: 96
readData: 98
Process finished with the exit code 0