通道(channel)是Golang中协程之间通信的重要方式
协程
- Golang中的协程和其它语言中定义的协程不太一样,Golang中的协程更倾向于并行;
协程之间的通信
- 协程之间通过channel进行通信,对于未指定缓冲的channel来说必须等待其它的协程来写或读才能继续执行剩下的语句(生产-消费),这也是协程之间阻塞的方式。
- channel的使用:
package main import ( "fmt" "time" ) func channelWrite(ch chan int){ ch<-5//写入数据 fmt.Println("I've written it.")//写入以后阻塞,直到被读出以后才能执行打印语句 } func channelRead(ch chan int){ time.Sleep(3*1e9) fmt.Println("Read from a channel",<-ch)//读取数据以后才能解除阻塞 } func main(){ ch:=make(chan int) go channelWrite(ch) go channelRead(ch) time.Sleep(5*1e9) }
- 带缓冲的channel:
前面的阻塞方式效率相对较低,可以通过指定缓冲区,只有当缓冲区满时才阻塞package main import ( "fmt" "time" ) func channelWrite(ch chan int){ ch<-5//写入数据 fmt.Println("I've written it.")//缓冲区未满,因此直接执行打印语句 } func channelRead(ch chan int){ time.Sleep(3*1e9) fmt.Println("Read from a channel",<-ch)//缓冲区未满,无法阻塞 } func main(){ ch:=make(chan in,5)//指定缓冲size=5 go channelWrite(ch) go channelRead(ch) time.Sleep(5*1e9) }
- channel的使用:
协程的关闭:
- 协程在不需要再写入数据时需要关闭通道,使用close(ch)即可手动关闭一个通道,应当在每次创建一个通道时defer close(ch),避免错误的产生
- 为了避免读取到已关闭的通道,每次读取操作时应当判断通道是否关闭:
if v,ok:=<-ch;ok{ ... }
协程的切换/选择:
- 在生产者消费者模型中比较常见的就是多个生产者生产不同的物品,多个消费者消费不同的物品,这种场景下通常使用select处理不同协程之间的通信
func writeChan1(ch1 chan int){ for i:=0;i<100;i++ { ch1 <-i } } func writeChan2(ch2 chan int){ for i:=0;i<100;i++ { ch2 <-i } } func selectFunc(ch1,ch2 chan int){ for{ select{ case val:=<-ch1: fmt.Printf("Got %v from channel 1\n",val) case val:=<-ch2: fmt.Printf("Got %v from channel 2\n",val) } } } func main(){ ch1:=make(chan int) ch2:=make(chan int) defer close(ch1) defer close(ch2) go writeChan1(ch1) go writeChan2(ch2) go selectFunc(ch1,ch2) time.Sleep(1e9) }
- select 执行类似switch,当所有的channel没有数据写入时,select阻塞直到有任何一个满足执行