Go 信道(channel)
什么是信道?
信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。
信道的声明
package main import ( "fmt" "time" ) //不同goroutine之间通信 //通过channel实现 func main() { //1 定义channel var c chan int //2 信道的零值(引用类型,空值为nil,当做参数传递时,不需要取地址,改的就是原来的,需要初始化再使用) fmt.Println(c) //3 信道初始化 c=make(chan int) //数字暂时先不关注 //4 信道的放值 (注意赋值和放值) //c<-1 //c=12 //赋值报错 //5 信道取值 //<-c //6 取出来赋值给一个变量 int //var a int //a=<-c //a:=<-c //7 信道默认不管放值还是取值,都是阻塞的 //c是引用类型 go test1(c) a:=<-c //阻塞 不但实现了两条协程之间通信,还实现了等待协程执行结束 fmt.Println(a) } func test1(a chan int) { fmt.Println("go go go ") time.Sleep(1*time.Second) //往信道中放一个值 a<-10 //阻塞 }
信道小例子
package main //信道小例子 //程序有一个数中 每一位的平方和与立方和,然后把平方和与立方和相加并打印出来 import ( "fmt" "time" ) func calcSquares(number int, squareop chan int) { sum := 0 //总和 for number != 0 { digit := number % 10 //589对10取余数,9 8 5 sum += digit * digit //sum=9*9 8*8 5*5 number /= 10 //num=58 5 0 } time.Sleep(2*time.Second) squareop <- sum } func calcCubes(number int, cubeop chan int) { sum := 0 for number != 0 { digit := number % 10 sum += digit * digit * digit number /= 10 } time.Sleep(1*time.Second) cubeop <- sum } //func calcSquares(number int, squareop chan int) int{ // sum := 0 //总和 // for number != 0 { // digit := number % 10 //589对10取余数,9 8 5 // sum += digit * digit //sum=9*9 8*8 5*5 // number /= 10 //num=58 5 0 // } // time.Sleep(2*time.Second) // return sum //} // //func calcCubes(number int, cubeop chan int) int{ // sum := 0 // for number != 0 { // digit := number % 10 // sum += digit * digit * digit // number /= 10 // } // time.Sleep(2*time.Second) // return sum //} func main() { ctime:=time.Now().Unix() fmt.Println(ctime) number := 589 sqrch := make(chan int) cubech := make(chan int) //num1:=calcSquares(number, sqrch) //num2:=calcCubes(number, cubech) go calcSquares(number, sqrch) go calcCubes(number, cubech) squares, cubes := <-sqrch, <-cubech //squares:= <-sqrch //cubes:=<-cubech fmt.Println("Final output", squares + cubes) ttime:=time.Now().Unix() fmt.Println(ttime) fmt.Println(ttime-ctime) }
信道的死锁现象
package main import "fmt" //信道的死锁现象,默认都是阻塞的,一旦有一个放,没有人取 或者一个人取,没有人放,就会出现死锁 //func main() { // //var c chan int =make(chan int ) // // //c<-1 //其实放不进去,阻塞在这,就死锁了 // //<-c //没有,取不到,阻塞在这,就死锁了 // //} //单向信道 //func sendData(sendch chan<- int) { // sendch <- 10 //} // //func main() { // //sendch := make(chan<- int) //定义了一个只写信道 // sendch := make(chan int) //定义了一个可读可写信道 // go sendData(sendch) //传到函数中转成只写信道,在goroutine中,只负责写,不能往外读,主协程读 // fmt.Println(<-sendch) //只写信道一旦读,就有问题 //} ///3 关闭信道 //func main() { // sendch := make(chan int) // //关闭信道 // //close(sendch) // // //信道可以用for循环循环 //} // 信道关闭close(sendch) ,for循环循环信道,如果不关闭会报死锁,如果关闭了,放不进去,循环结束 func producer(chnl chan int) { for i := 0; i < 100; i++ { fmt.Println("放入了",i) chnl <- i } close(chnl) } func main() { ch := make(chan int) go producer(ch) for v := range ch { fmt.Println("Received ",v) } }
缓冲信道
package main import ( "fmt" "sync" "time" ) //缓冲信道:只在缓冲已满的情况,才会阻塞向缓冲信道,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据 //func main() { // var c chan int =make(chan int,6) //无缓冲信道数字是0 // c<-1 // c<-2 // c<-3 // c<-4 // c<-5 // c<-6 // //c<-7 //死锁 // <-c // <-c // //<-c // //<-c // //<-c // //<-c // //<-c // 取空了,死锁(一个goroutine中会出现) // // // // 2 长度 vs 容量 // //fmt.Println(len(c)) //目前放了多少 // //fmt.Println(cap(c)) //可以最多放多少 // // // //} //3 WaitGroup 等待所有goroutine执行完成 //4 使用信道如何实现? func process1(i int,wg *sync.WaitGroup) { fmt.Println("started Goroutine ", i) time.Sleep(2 * time.Second) fmt.Printf("Goroutine %d ended\n", i) //一旦有一个完成,减一 wg.Done() } func main() { var wg sync.WaitGroup //没有初始化,值类型,当做参数传递,需要取地址 //fmt.Println(wg) for i:=0;i<10;i++ { wg.Add(1) //启动一个goroutine,add加1 go process1(i,&wg) } wg.Wait() // 一直阻塞在这,知道调用了10个done,计数器减到零 }