关于 Golang 的 channel

在 golang 中,channel 是一个非常重要的工具,用来在 goroutine 之间进行通信。channel 本身可以带缓存也可以不带缓存。channel 支持接收和发送两个行为,用于传递数据。

创建 channel

可以使用 make 创建一个 channel。

ch := make(chan int) // 创建一个可以发送和接收 int 类型数据的不带缓存的 channel
ch := make(chan int, 10) // 这是个带缓存的 channel,容量为 10

发送和接收数据

向 channel 发送数据和从 channel 接收数据都会用到操作符 <-

a =<- ch // a 将从 ch 中接收数据
ch <- a // 将 a 的内容发送至 ch
// 很多时候接收数据不是一次性的,而是一个连续的行为,所以可以用下面的方式接收
// 方法
for i := range ch{
  // range 会一直迭代 ch 中的值,直到 ch 被 close
}
// 同样的,读和写都可以配合 select 操作。

阻塞

向一个容量满了的的 channel 发送数据会导致阻塞,直至数据被接收者取走。同样,从空的 channel 里取数据也会导致阻塞, 直至发送者发送数据到 channel。

不带缓存的 channel 就是一个容量为 0 的channel,只要写了数据没有被读就会阻塞。

在 select 语句中,如果发生阻塞,会进入 default case。

关闭

close(ch) 语句可以关闭一个 channel。

func main() {
    ch := make(chan int, 5)
    ch <- 1
    a, ok := <-ch
    fmt.Println(a, ok)

    close(ch)
    a, ok = <-ch
    fmt.Println(a, ok)
    ch <- 2
}

// output
// 1 true
// 0 false
// panic: send on closed channel
// 
// goroutine 1 [running]:
// main.main()
//     /Users/Laily/go/src/test/t1/t1.go:16 +0x1e9
// exit status 2

向一个被 closed 的 channel 发送数据会引起 panic,从一个 closed 的 channel 接收数据,会得到该数据类型的 0 值。

接收数据的时候第二个值表示 channel 是否开着。

关于发送数据时无法知道 channel 是否关闭的情况,只能在程序中自己判断。可以设置一个标志用来记录 channel 是否被关闭。或者干脆不关闭。

官方说法是只有在需要告诉接收者所有数据已经发送完成的时候才需要关闭 channel,其余时候可以不关闭,GC 会自行销毁掉无用的 channel(当然,如果有 channel 的引用一直存在还是会导致内存泄漏)。