天天看點

管道(Channel)的讀取與寫入「讓我們一起Golang」

管道(Channel)的讀取與寫入「讓我們一起Golang」

我們都知道,協程是通過管道來進行通信、排程的。是以接下來我們引入管道的概念,通過管道可以來傳遞資料,協程與協程之間也可以通過管道來進行排程。

先來看一看第一段代碼:

func main() {
    //建立一個管道
    ch := make(chan int)
    //子協程讀資料
    go func() {
        x := <-ch
        fmt.Println("從管道内讀資料:",x)
    }()
    //因為ch是int整形管道,往管道ch内隻可寫入整形資料,例如123。
    //主協程寫資料
    ch <- 123
    time.Sleep(time.Second)
    fmt.Println("GAMEOVER")
}           

這裡是建立一個管道,然後用主協程往管道内寫資料,然後從子協程往管道内讀資料。

建立管道是用make,第一部分是

chan

,第二個部分是管道内資料的資料類型。因為沒有給管道制定長度,是以預設為0。是以不能用于緩存。

該段程式是主協程往管道内寫入123,然後子協程從管道内讀出123.

運作結果是:

從管道内讀資料: 123
GAMEOVER           

那麼思考一下,如果此段代碼中将

time.Sleep(time.Second)

注釋掉,也就是不使用這句代碼不讓主協程睡一秒,會出現什麼情況?

是的,如果主協程不睡一秒的話,子協程可能還沒讀到資料,主協程就結束了,注意,主協程不是被殺死了,是正常結束了。

如果不睡一秒,而是使用

runtime.Goexit()

殺掉主協程,那麼子協程就會失去限制,仍然會輸出管道的資料。

如果主協程不寫的話,我們從管道中讀不到資料這樣可以了解,但是你可能想不到的是,如果子協程不讀的話,主協程也不能将資料成功寫入管道中。

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()           

它會報一個緻命錯誤,它預判會産生死鎖。

這說明管道不能存東西,它是沒有緩存能力的,隻能用于傳輸資料。

下面來講一講管道關閉之後的讀寫。

//定義管道
    var ch chan int
    //make才能初始化
    ch  := make(chan int)
    //關閉管道
    close(ch)           

如果隻定義管道,那麼管道

ch

為nil,此時不能關閉管道,此時關閉管道會報錯。

隻有将管道

ch

初始化之後,才能正常關閉管道。

不能重複關閉管道。

關閉會報錯:

panic: close of closed channel           

我們再來看一看關閉管道後再來讀會怎麼樣咯~

func main()  {
    //定義管道
    var ch chan int
    //初始化管道,緩存能力為3
    ch  = make(chan int , 3)
    ch <- 123
    //關閉管道
    close(ch)
    go func() {
        x := <-ch
        fmt.Println("讀到",x)

        //x,ok := <-ch
        //fmt.Println("讀到",x,ok)
    }()

    time.Sleep(time.Second)
    fmt.Println("GAME OVER")
}           

此段代碼主協程中先關閉管道,然後再開辟子協程來讀取管道中的資料。能不能讀到呢?

先看一看結果:

讀到 123
GAME OVER           

讀到了!

因為我們給管道的第二個參數設定為3,這就讓管道有了緩存能力。而關閉管道之前已經将資料123存入了管道,之後再讀取管道内資料是能夠讀取到的。

可以如果我們讀取之後,再讀一遍呢?會怎麼樣呢?

我們激活下面這段代碼

x,ok := <-ch
fmt.Println("讀到",x,ok)           

得到的運作結果是:

讀到 123讀到 0 falseGAME OVER           

則說明讀取管道内的資料之後繼續再讀一遍是讀到的預設資料0,無論你再讀多少遍,讀到都還是0。

如果管道關閉,我們還能像裡面寫資料嗎?

是的,不能再往管道寫資料了。

如果我們往管道裡面寫n個,那麼關閉管道之後,再去讀管道,就能再讀n個,超過n個就讀到的是預設值0。