首頁 >後端開發 >Golang >詳解 Golang 頻道 Chan

詳解 Golang 頻道 Chan

藏色散人
藏色散人轉載
2020-11-09 15:40:542643瀏覽

#下面由golang教學專欄介紹關於Golang 關於頻道Chan 詳解,希望對需要的朋友有幫助!

詳解 Golang 頻道 Chan

首先我們來看線程,在golang裡面也叫goroutine

在讀這篇文章之前,我們需要了解一下並發與並行。 golang的執行緒是一種並發機制,而不是並行。它們之間的差異大家可以上網搜一下,網路上有很多的介紹。

下面我們先來看一個例子吧

import(
         "fmt"
)

funcmain(){

    go fmt.Println("1")
    fmt.Println("2")    
}

在golang裡面,使用go這個關鍵字,後面再跟上一個函數就可以建立一個執行緒。後面的這個函數可以是已經寫好的函數,也可以是一個匿名函數

funcmain(){    var i=3    go func(a int) {        fmt.Println(a)
        fmt.Println("1")
    }(i)
    fmt.Println("2")}

上面的程式碼就創建了一個匿名函數,並且還傳入了一個參數i,下面括號裡的i是實參,a是形參。

那麼上面的程式碼可以按照我們預想的列印1、2、3嗎?告訴你們吧,不能,程式只能印出2。下面我把正確的程式碼貼出來吧

import(    "fmt"
    "time"    )funcmain(){    var i = 3    go func(a int) {        fmt.Println(a)
        fmt.Println("1")
    }(i)
    fmt.Println("2")
    time.Sleep(1 * time.Second)}

我只是在最後加了一行讓主執行緒休眠一秒的程式碼,程式就會依

次印出2、3、1 。
那為什麼會這樣呢?因為程式會優先執行主線程,主線程執行完成後,程式會立即退出,沒有多餘的時間去執行子線程。如果在程式的最後讓主執行緒休眠1秒鐘,那麼程式就會有足夠的時間去執行子執行緒。

線程先講到這裡,我們來看看通道吧。

通道又叫channel,顧名思義,channel的作用就是在多執行緒之間傳遞資料的。

建立無緩衝channel

chreadandwrite :=make(chan int)

chonlyread := make(<-chan int) //建立唯讀channel
chonlywrite := make(chan<- int) //建立只寫channel
下面我們來看一個例子:

    ch :=make(chan int)     
    ch <- 1
      go func() {
        <-ch
        fmt.Println("1")
      }()
      fmt.Println("2")

這段程式碼執行時會出現一個錯誤:fatal error: all goroutines are asleep - deadlock!

這個錯誤的意思是說執行緒陷入了死鎖,程式無法繼續往下執行。那麼造成這種錯誤的原因是什麼呢?

我們創造了一個無緩衝的channel,然後給這個channel賦值了,程式就是在賦值完成後陷入了死鎖。因為我們的channel是無緩衝的,也就是同步的,賦值完成後來不及讀取channel,程式就已經阻塞了。這裡介紹一個很重要的概念:channel的機制是先進先出,如果你給channel賦值了,那麼必須讀取它的值,不然就會造成阻塞,當然這個只對無緩衝的channel有效。對於有緩衝的channel,發送方會一直阻塞直到資料被拷貝到緩衝區;如果緩衝區已滿,則發送方只能在接收方取走資料後才能從阻塞狀態恢復。

對於上面的例子有兩種解決方案:

1、增加channel緩衝區,然後在程式的最後讓主執行緒休眠一秒,程式碼如下:

    ch :=make(chan int,1)
    ch <- 1
    go func() {
        v := <-ch
        fmt.Println(v)
    }()
    time.Sleep(1 * time.Second)
    fmt.Println("2")

這樣的話程式就會依序印出1、2

2、把ch<-1這一行程式碼放到子執行緒程式碼的後面,程式碼如下:

    ch :=make(chan int)    go func() {
        v := <-ch
        fmt.Println(v)
    }()
    ch <- 1
    fmt.Println("2")

這裡就不用讓主執行緒休眠了,因為channel在主執行緒中被賦值後,主執行緒就會阻塞,直到channel的值在子執行緒中被取出。

最後我們來看一個生產者和消費者的例子:

import (    "fmt"
    "time")func produce(p chan<- int) {    for i := 0; i < 10; i++ {
        p <- i
        fmt.Println("send:", i)
    }
}func consumer(c <-chan int) {    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("receive:", v)
    }
}func main() {
    ch := make(chan int)    go produce(ch)    go consumer(ch)
    time.Sleep(1 * time.Second)
}

在這段程式碼中,因為channel是沒有緩衝的,所以當生產者給channel賦值後,生產者這個執行緒會阻塞,直到消費者執行緒將channel中的資料取出。消費者第一次取出資料後,進行下一次循環時,消費者的執行緒也會阻塞,因為生產者還沒有將資料存入,這時程式會去執行生產者的執行緒。程式就這樣在消費者和生產者兩個執行緒間不斷切換,直到循環結束。

下面我們再看一個有緩衝的例子:

import (    "fmt"
    "time")func produce(p chan<- int) {    for i := 0; i < 10; i++ {
        p <- i
        fmt.Println("send:", i)
    }
}func consumer(c <-chan int) {    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("receive:", v)
    }
}func main() {
    ch := make(chan int, 10)    go produce(ch)    go consumer(ch)
    time.Sleep(1 * time.Second)
}

在這個程式中,緩衝區可以儲存10個int型別的整數,在執行生產者執行緒的時候,執行緒就不會阻塞,一次​​性將10個整數存入channel,在讀取的時候,也是一次性讀取。

#

以上是詳解 Golang 頻道 Chan的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除