每个通道都有与之关联的类型。此类型是允许通道传输的数据类型。不允许使用该通道传输其他类型的数据。
通道的零值为nil。零通道没有任何用处,因此必须使用类似于 map 和 slice 的make来定义。
package main
import "fmt"
func main() {
var a chan int // 定义 channel
if a == nil {
fmt.Println("channel a is nil, going to define it")
a = make(chan int)
fmt.Printf("Type of a is %T", a) // 输出Type of a is chan int
}
}
data := <- a // read from channel a
a <- data // write to channel a
默认情况下,发送和接收到通道处于阻塞状态。当数据发送到通道时,主goroutine将在send语句中被阻塞,直到其他Goroutine从该通道读取数据为止。同样,当从通道读取数据时,将阻止读取,直到某些Goroutine将数据写入该通道为止。
此属性可帮助Goroutines有效通信,而无需使用其他编程语言中很常见的显式锁或条件变量。
一个简单的程序:程序实现 (1 * 1) (2 * 2) (3 * 3) ... (n * n) (1 * 1 * 1) (2 * 2 * 2) (3 * 3 * 3) ...(n * n * n)
可以拆成两部分并发执行,一个 goroutine 计算平方和,一个计算立方和
package main
import (
"fmt"
)
func calcSquares(number int, squareop chan int) {
sum := 0
for number != 0 {
digit := number % 10
sum = digit * digit
number /= 10
}
squareop <- sum
}
func calcCubes(number int, cubeop chan int) {
sum := 0
for number != 0 {
digit := number % 10
sum = digit * digit * digit
number /= 10
}
cubeop <- sum
}
func main() {
number := 10
sqrch := make(chan int)
cubech := make(chan int)
go calcSquares(number, sqrch)
go calcCubes(number, cubech)
squares, cubes := <-sqrch, <-cubech // 利用 channel 使主 goroutine 等待结果
fmt.Println("Final output", squares cubes)
}
有一个地方需要注意,在使用 channel 时可能会引发死锁,看下面一个例子
func main() {
ch := make(chan int)
ch <- 5
}
这段程序向 channel 发送数字5,但是没有接收方,所以会一直阻塞
上述简单的例子展示了双向 channel 如何使用,但是也可以创建单向通道,即仅发送或接收数据的通道。
// 定义只发送channel
sendch := make(chan<- int)
但是这样做又有什么意义呢,无法从 channel 中读取数据。这是使用 channel 转换。可以将双向通道转换为仅发送或仅接收通道,反之亦然。
// 这里将 channel 转换为单项只发送
func sendData(sendch chan<- int) {
sendch <- 10
}
func main() {
// 定义一个双向 channel
chnl := make(chan int)
go sendData(chnl)
fmt.Println(<-chnl)
}
发送者可以关闭该通道,以通知接收者该通道将不再发送任何数据。接收器可以在从通道接收数据时使用附加变量,以检查通道是否已关闭。
v, ok := <- ch
在上面的语句中,如果该值是通过对通道的成功发送操作接收到的,则ok为真。如果ok为假,则表示我们正在从封闭的通道读取数据。从关闭的通道读取的值将是通道类型的零值。例如,如果通道是int通道,则从封闭通道接收的值将为0。
channel 和 for range 可以搭配使用
func producer(chnl chan int) {
for i := 0; i < 10; i {
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for v := range ch { // 若channel 未关闭,则一直取数据
fmt.Println("Received ",v)
}
}
如果喜欢,请关注我的公众号,或者查看我的博客 http://packyzbq.coding.me. 我会不定时的发送我自己的学习记录,大家互相学习交流哈~