1. 程式人生 > 其它 >Go 通道(channel)

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,計數器減到零
}