Golang並發(三) - Channel

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.

你所浪費的今天是昨天死去的人奢望的明天; 你所厭惡的現在是未來的你回不去的曾經。

 

Channel通道介紹

    什麼是channel, channel可以理解為goroutine之間通訊的通道。一端發送資料方到達接收資料方的橋樑。

 

聲明通道

    每個channel都有一個與之相關聯的類型, 這種類型是通道允許傳輸的資料類型。

    channel的零值是nil, 一個nil channle沒有任何用途。因此必須使用類似map和slice的方式定義。

package mainimport "fmt"func main() {      var A chan int    if A == nil {        fmt.Println("nil channle ,定義channle")        A = make(chan int)        fmt.Printf("Type of A is %T", A)    }}

 

channel的發送和接收

chs := make(chan int)chs <- 0   // 寫入i := <- chs  // 接收

 

發送、接收阻塞

    channel的發送和接收預設是阻塞的。當從channel讀取資料時, main在讀取時阻塞,知道有資料寫入channel, 通道的發送也是如此。

    通道的這個屬性可以使goroutine有效地進行通訊,而無需使用像其他程式設計語言中,很常見的顯式鎖或條件變數。

 

channel的使用

package mainimport (      "fmt"    "time")func hello() {      fmt.Println("Hello goroutine")}func main() {      go hello()    time.Sleep(1 * time.Second)    fmt.Println("main function")}

    在這裡我們使用sleep main進程的方式,阻塞main的繼續執行,好讓goroutine有時間執行 hello()內的內容。

    但是這樣做並不能有效解決多個協程(main 和 goroutine)並行時出現的協程未執行完,main程退出問題。 如果hello()中的執行時間超過1S, 那麼sleep就無實際意義了。

    下面使用channle來改善以上程式:

package mainimport ("fmt")var done chan boolfunc hello() {fmt.Println("Hello world goroutine")done <- true}func main() {done = make(chan bool)go hello()<-donefmt.Println("main function")}

    首先main程會初始化一個channle, 然後go hello() 建立一個新的協程,並立即執行<-done (前面我們說過,只是簡單的調用,並不關心協程的輸出,而立刻返回繼續往下進行),並沒有發送資料到channel done,所以main程阻塞。直到goroutine執行完成並發送資料到channle,main才會繼續執行輸出。

    還有一點要注意的是:channel的接受可以不用賦值到任何變數,即有效阻塞。

    下面的程式會更好的理解channle:

package mainimport ("fmt""time")func hello(done chan bool) {fmt.Println("go hello() sleep ...")time.Sleep(4 * time.Second)fmt.Println("go hello() awake")done <- true}func main() {done := make(chan bool)fmt.Println("Main going to call hello go goroutine")go hello(done)<-donefmt.Println("Main received data")}

    程式會在goroutine中sleep後繼續寫入channle。

package mainimport ("fmt")func add(number int, addChs chan int) {number = number + 10addChs <- number}func reduce(number int, reduceChs chan int) {number = number - 10reduceChs <- number}func main() {number := 589addChs := make(chan int)reduceChs := make(chan int)go add(number, addChs)go reduce(number, reduceChs)addVar, reduceVar := <-addChs, <-reduceChsfmt.Println(addVar, reduceVar)}

 

 

死結

    當程式中只有channle的讀取或者發送的其中一個操作, 那麼程式就會發生Deadlock。

package mainfunc main() {ch := make(chan int)<-ch}

fatal error: all goroutines are asleep - deadlock!

 

單向通道

    channel既可以發送也可以接收, 我們成為雙向channle。 如果channle只能接收或者發送,就稱為單向通道。

    執行個體code說明一切:

package mainimport "fmt"func sendData(sendch chan<- int) {      sendch <- 10}func main() {      sendch := make(chan<- int)    go sendData(sendch)    fmt.Println(<-sendch)}

    在mian程開始時,我們定義了一個唯寫(只發送)的channel, 但是後面main程又從中讀取。所以導致go程式編譯失敗。

    在上面我們看到sendData()接收到一個唯寫的channel ,那麼如果我們傳入的是雙向的channel會發生什嗎?   

package mainimport "fmt"func sendData(sendch chan<- int) {      sendch <- 10}func main() {      chnl := make(chan int)    go sendData(chnl)    fmt.Println(<-chnl)}

    程式照常執行輸出。為什嗎?

    可以這麼簡單的理解一下:  因為在方法內(像java 範圍)sendData限制了sendch通道的讀取,只能對channle進行寫入操作,但是main程不屬於同一範圍,sendData限制無效,main繼續讀取操作channle。

 

通道的關閉

    發送端可以關閉通道,以便告訴接收方,不再發送資料。

    接收方也可以在接收的同時,通過附加變數的方式檢測channle的關閉狀態。

v, ok := <- ch  

    如果開啟狀態下,ok為true, 否則為false。 

    在channel為關閉狀態時, v(接收到的值)也是有值的,即channle type( 通道類型 )的零值。 比如 make(chan int) 零值就是0,  make(chan string)零值就是"", make(chan map[string]int)呢?大家可以聯想一下。

    

package mainimport (      "fmt")func producer(chnl chan int) {      for i := 0; i < 10; i++ {        chnl <- i    }    close(chnl)}func main() {      ch := make(chan int)    go producer(ch)    // 試試將此處改用range代替    for {        v, ok := <-ch        if ok == false {            break        }        fmt.Println("Received ", v, ok)    }}

前面我們說過,<- channel 通道的讀取也會使程式阻塞,那麼range操作呢?

package mainimport ("fmt")func options(number int, dchnl chan int) {for i:= 0 ;i< 10 ;i++ {digit := number + idchnl <- digit}close(dchnl)}func box(number int, s chan int) {sum := 0dch := make(chan int)go options(number, dch)for digit := range dch {sum += digit + digit}s <- sum}func main(){s := make(chan int)go box(1, s)sVal := <-sfmt.Println("Final output: ",sVal)}

range讀取channle同樣可以阻塞程式,並不是單單<-channle。

 

The End.

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.