Go之路(二十五):Channel
Channel
协程之间的通信有两种方式
1.全局变量和锁
2.channel
上个章节中用到的就是全局变量和锁的方式来操作一个公共数据
channel的概念
channel已经实现了线程安全,不需要额外加锁了
channel的基本用法:
package main import( "fmt" ) type Student struct{ Name string Age int } func main() { var intChan chan interface{} intChan = make(chan interface{}, 10) stu1 := Student{ Name:"tom", Age: 13, } intChan <- stu1 stu2 := <- intChan var stu3 interface{} stu3, ok := stu2.(Student) if !ok{ fmt.Println("NO") } fmt.Println(stu3) }
虽然channel需要指明类型,但是如果指定为空接口的话就可以传输任意类型的数据了
另外可以用断言来转换数据
例子2:判断一万以内的质数
package main import( "fmt" "time" ) func cal(a chan int, result chan int){ for v := range a{ flag := true for i:=2;i<v;i++{ if v%i==0{ flag = false break } } if flag{ result <- v } } } func main() { var task = make(chan int,100) var result = make(chan int,100) go func (){ for i:=0;i<10000;i++{ task <- i } close(task) }() for i:=0;i<4;i++{ go cal(task,result) } go func(){ for v:= range result{ fmt.Println(v) } }() time.Sleep(time.Second*5) }
1.管道可以关闭,关闭后不能继续放值
2.可以用for range语句来取管道内的值
3.可以用ok来判断管道是否关闭
v,ok := <-task if ok == false{ fmt.Println("管道已经关闭") }
如果for{}不加条件,然后里面是
v,ok := <-task,就会出现下面的情况
不关闭管道一直取值取到空的时候是会阻塞的,但是一旦关闭管道,取完值后再继续取值就会一直取到0(空值)
但是如果是for range 取完就自动退出了
可以再用一个管道来确认目标协程是否执行完
例子:
package main import( "fmt" "math/rand" ) func cal(a chan int, result chan int,exist chan int){ for v := range a{ flag := true for i:=2;i<v;i++{ if v%i==0{ flag = false break } } if flag{ result <- v } } fmt.Println("exit") exist <- rand.Intn(10) } func main() { var task = make(chan int,100) var result = make(chan int,100) var exist = make(chan int,4) go func (){ for i:=0;i<10000;i++{ task <- i } close(task) }() for i:=0;i<4;i++{ go cal(task,result,exist) } go func (){for i:=0;i<4;i++{ b := <- exist fmt.Println(b) } close(result) }() for _= range result{ // fmt.Println(v) } }
但是要注意这个确认的需要另外起一个协程,因为go的自检测机制会认为这个是死锁
非阻塞以及多路复用可以用select
select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。
select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。
例子:
package main import( "fmt" "time" ) func main() { var a = make(chan int,5) var b = make(chan int,5) go func(){ var i int for { a <- i time.Sleep(time.Second) b <- i*i time.Sleep(time.Second) i++ } }() for{ select{ case v := <- a: fmt.Println(v) case v:= <- b: fmt.Println(v) default: time.Sleep(time.Second) fmt.Println("?") } } }
只读 <-chan 只写chan<-