Go 通道(channel)
阿新 • • 發佈:2021-06-15
什麼是通道?
通道可以想像成 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,計數器減到零 }