channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。
通过调用内置的close函数来关闭通道。
close(ch)
关闭通道相当于通知接收方所有的数据都发送完毕。
只有所有goroutine包括主线程的接收者全部阻塞才会死锁(这里的阻塞不是指死循环)。如果发送完数据之后没有关闭通道,但还有别的协程在跑就不会死锁,不过接收者的协程会一直阻塞。
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
}()
go func() {
for {
}
}()
for data := range c {
fmt.Println(data)
}
}
关闭后的通道有以下特点:
对一个关闭的通道再发送值就会导致panic。
对一个关闭的通道进行接收会一直获取值直到通道为空。
对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
关闭一个已经关闭的通道会导致panic。
无缓冲的通道又称为阻塞的通道,只有在有人接收的时候才能发送,也只有在有人发送的时候才能接收(但如果在已经关闭的情况会得到零值)。
无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。同样的,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。
使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。
我们可以在使用make函数初始化通道的时候为其指定通道的容量,例如:
func main() { ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道 ch <- 10 fmt.Println("发送成功") }
只要通道的容量大于零,该通道就是有缓冲的通道。就像你快递柜只有那么个多格子,格子装满了就阻塞了,等到别人取走一个才能往里面再放一个。
用协程演示的埃氏筛素数,然而效率其实不高,虽然用带缓冲的通道减少了阻塞等待的时间,但毕竟是用channel大量传输数据
package main
import (
"fmt"
"time"
)
func main() {
start := time.Now()
ch := make(chan int, 64)
go func(cur chan<- int) { // 浅拷贝是对地址的拷贝,如果之后ch指向的地址变了,cur还是不变
defer close(cur)
for i := 2; i < 1e5; i++ {
cur <- i
}
}(ch)
for { // 每次循环取出当前ch中的第一个数,即为下一个素数
prime, ok := <-ch
if !ok {
break
}
next := make(chan int, 64)
go func(in <-chan int, out chan<- int, prime int) {
defer close(out)
for i := range in { // 埃氏筛素数
if i%prime != 0 {
out <- i
}
}
}(ch, next, prime)
ch = next // 筛出的数作为下一轮的输入
}
fmt.Println(time.Now().Sub(start))
}