Channel是Go中的一个核心类型,你能够把它当作一个管道,经过它并发核心单元就能够发送或者接收数据进行通信(communication)。面试
T表示任意的一种类型并发
单向的channel,不只能够经过声明make(chan <- interface{})
来建立,还能够经过隐身或显示的经过 chan
来转换,以下函数
func main() { channel := make(chan int, 10) convert(channel) } func convert(channel chan<- int) {}
在 convert函数中,就能够吧channel当成单向输入管道来使用了
code
既然 双向 chan,既能够接收,也能够发送,为何还会有单向chan的存在? 个人一个理解即是 权限收敛,例如一个爬虫系统中,有些进程a仅仅负责抓取页面内容,并转发给进程b,那进程a仅须要 单向发送的chan
便可进程
缺省状况下,发送chan或接收chan会一直阻塞着,直到另外一方准备好。这种方式能够用来在gororutine中进行同步,而没必要使用显示的锁或者条件变量。同步
如官方的例子中x, y := <-c, <-c
这句会一直等待计算结果发送到channel中。如下面例子看一下io
func bufferChannel() { channel := make(chan int) i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i)① channel <- i fmt.Printf("send %d to channel\n", i)② }(i) time.Sleep(2 * time.Second) fmt.Println("sleep 2 second") value := <-channel③ fmt.Println("got ", value) }
输出结果以下class
start goroutine 0 sleep 2 second got 0 send 0 to channel
能够看出,go func
执行到了①后并无继续执行②,而是等待③执行完成后,再去执行②,也就能够说明 channel <- i
阻塞了goroutine
的继续执行变量
若是,我不想在这里阻塞,而是我直接把数据放到channel
里,等接收方准备好后,到channel
中自取自用如何处理,这里就涉及到了另外一个概念 buffered channelsed
咱们把程序修改一下
func bufferChannel() { channel := make(chan int, 1) // 这里加了个参数 i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i)① channel <- i fmt.Printf("send %d to channel\n", i)② }(i) time.Sleep(2 * time.Second) fmt.Println("sleep 2 second") value := <-channel③ fmt.Println("got ", value) }
输出结果
start goroutine 0 send 0 to channel sleep 2 second got 0
咱们发现go func
执行完①以后就执行了②,并无等待③的执行结束,这就是buffered channel的效果了
咱们只须要在make的时候,声明底2个参数,也就是chan的缓冲区大小便可
经过上面的程序能够看出,咱们一直在使用③的造成,即<- chan
来读取chan中的数据,可是若是有多个goroutine在同时像一个chan写数据,咱们除了使用
for { value <- chan }
还有什么更优雅的方式吗
仍是上面那个程序,咱们使用 for … range 进行一下改造
func bufferChannel() { channel := make(chan int, 1) i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i) channel <- i fmt.Printf("send %d to channel\n", i) }(i) time.Sleep(2 * time.Second) fmt.Println("sleep 2 second") for value := range channel { fmt.Println("got ", value) } }
这样就能够遍历 channel
中的数据了,可是咱们在运行的时候就会发现,哎 这个程序怎么停不下来了?range channel
产生的迭代值为Channel中发送的值,它会一直迭代直到channel被关闭,因此 咱们goroutine
发送完数据后,把channel
关闭一下试试,这一次,咱们再也不进行time.Sleep(2 * time.Second)
func bufferChannel() { channel := make(chan int, 1) i := 0 go func(i int) { fmt.Printf("start goroutine %d\n", i) channel <- i fmt.Printf("send %d to channel\n", i) close(channel) }(i) for value := range channel { fmt.Println("got ", value) } }
这样,整个程序就能够正常退出了,因此,在使用range
的时候须要注意,若是channel
不关闭,则range
会一直阻塞在这里的
咱们上面讲的一直都是只有一个channel
的时候,咱们应该怎么去作,加入有两个channel
或者更多的channel
,咱们应该怎么去作,这里就介绍一下 go里面的多路复用 select
,如下面程序为例
func example() { tick := time.Tick(time.Second) after := time.After(3 * time.Second) for { select { case <-tick: fmt.Println("tick 1 second") case <-after: fmt.Println("after 3 second") return default: fmt.Println("come into default") time.Sleep(500 * time.Millisecond) } } }
time.Tick
是go的time包提供的一个定时器的一个函数,它返回一个channel
,并在指定时间间隔内,向channel发送一条数据,time.Tick(time.Second)
就是每秒钟向这个channel发送一个数据
time.After
是go的time包提供的一个定时器的一个函数,它返回一个channel
,并在指定时间间隔后,向channel发送一条数据,time.After(3 * time.Second)
就是3s后向这个channel发送一个数据
输出结果
come into default come into default tick 1 second come into default come into default tick 1 second come into default come into default tick 1 second after 3 second
能够看到,select
会选择一个没有阻塞的 channel
,并执行响应 case
下的逻辑,这样就能够避免因为一个 channel
阻塞而致使后续的逻辑阻塞的状况了
咱们继续作个小实验,把上面关闭的channel
放到 select
里面试一下
func example() { tick := time.Tick(time.Second) after := time.After(3 * time.Second) channel := make(chan int, 1) go func() { channel <- 1 close(channel) }() for { select { case <-tick: fmt.Println("tick 1 second") case <-after: fmt.Println("after 3 second") return case value := <- channel: fmt.Println("got", value) default: fmt.Println("come into default") time.Sleep(500 * time.Millisecond) } } }
输出结果
. . . . got 0 got 0 got 0 got 0 got 0 after 3 second
简直是车祸现场,幸亏设置了3s主动退出,那case的时候,有没有办法判断这个channel是否关闭了呢,固然是能够的,看下面的程序
func example() { tick := time.Tick(time.Second) after := time.After(3 * time.Second) channel := make(chan int, 1) go func() { channel <- 1 close(channel) }() for { select { case <-tick: fmt.Println("tick 1 second") case <-after: fmt.Println("after 3 second") return case value, ok := <- channel: if ok { fmt.Println("got", value) } else { fmt.Println("channel is closed") time.Sleep(time.Second) } default: fmt.Println("come into default") time.Sleep(500 * time.Millisecond) } } }
输出结果
come into default got 1 channel is closed tick 1 second channel is closed channel is closed after 3 second
综上能够看出,经过 value, ok := <- channel
这种形式,ok获取的就是用来判断channel
是否关闭的,ok为 true,表示channel正常,不然,channel就是关闭的