Go 语法笔记 03 | 青训营

113 阅读3分钟

本篇笔记是笔者在第六届字节跳动青训营期间学习 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