golang的Channel

golang的Channel

Channel 是 golang 一個很是重要的概念,若是你是剛開始使用 golang 的開發者,你可能尚未真正接觸這一律念,本篇咱們將分析 golang 的Channelgolang

1. 引入

要講 Channel 就避不開 Goroutine -- 協程。閒話不說, 直接上個例子緩存

Goroutine demo安全

package main

import (
    "fmt"
    "time"
)

func main() {
    origin := 1
    go doSomeCompute(origin)
    time.Sleep(5 * time.Second)
}

func doSomeCompute(num int) {
    result := num * 2
    fmt.Println(result)
    return
}

簡單來講,例子中有一個 main 的協程,一個 doSomeCompute 的協程。還有個延時退出的方法等待計算協程計算結果。咱們嘗試思考這個例子的一些問題:數據結構

a. 如何獲取 doSomeCompute 的計算結果?函數

b. 如何獲取 doSomeCompute 的執行狀態,當前是執行完了仍是執行中?ui

如何解決這種問題呢?線程

Channel!code

2. Channel

Channel怎麼處理上面的問題?咱們直接上代碼:協程

舉例blog

package main

import (
    "fmt"
)

func main() {
    origin := 1
    //一個無緩衝Channel
    res := make(chan int)
    go doSomeCompute(origin, res)
    fmt.Println(<-res)
}

func doSomeCompute(num int, res chan int) {
    result := num * 2
    res <- result
}

例子中,Channel 充當起了協程間通訊的橋樑。Channel 能夠傳遞到協程說明它是線程安全的,事實也是如此。Channel 能夠理解爲管道,協程 doSomeCompute 向 Channel 寫入結果, main 中讀取結果。注意,例子中讀取 Channel 的地方會阻塞直到拿到計算結果,這樣就解決了問題 a 和 b。

2. Channel 的方向性

上面的例子中,計算協程是負責計算並將計算結果寫入 Channel ,若是咱們但願保證計算協程不會從 Channel 中讀取數據該怎麼處理?很簡單,看例子:

func doSomeCompute(num int, res chan<- int) {
    result := num * 2
    res <- result
}

這個參數的聲明 chan<- int 就表示該函數只能講數據寫入 Channel ,而不能從中讀取數據。後面的 int 表示 Channel 中數據的格式。一樣的, 只能夠讀取數據的 Channel 能夠聲明爲 <-chan int 。而例子中不帶有方向聲明的 Channel 則既能夠寫入也能夠讀取。

3. 阻塞性質

Channel 的讀取和寫入操做在各自的協程內部都是阻塞的。好比例子中 fmt.Println(<-res) , 這一語句會阻塞直至計算協程將計算結果放入,能夠讀出。也就是說,協程會阻塞直至從 res 中讀出數據。

注意,無緩衝的 Channel 的讀寫都是阻塞的,有緩衝的 Channel 能夠一直向裏面寫數據,直到緩存滿纔會阻塞。讀取數據同理, 直至 Channel 爲空才阻塞。

用一個典型的例子來講明緩衝和非緩衝 Channel 的區別:

package main

import "fmt"

func doSomeCompute(ch chan int) {
    fmt.Println("deadlock test")
    <- ch
}

func main() {
    ch := make(chan int)
    ch <- 1
    go doSomeCompute(ch)
}

例子中,main 協程會向 ch 寫入數據, 這一過程是阻塞的,也就是說,doSomeCompute 協程沒法執行,程序死鎖。輸出以下:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    C:/mygo/src/demo/blog.go:12 +0x73
exit status 2

若是改爲有緩衝的 Channel :

package main

import (
    "fmt"
    "time"
)

func doSomeCompute(ch chan int) {
    fmt.Println("deadlock test")
    <- ch
}

func main() {
    ch := make(chan int, 1)
    ch <- 1
    go doSomeCompute(ch)
    time.Sleep(1 * time.Second)
}

有與有緩衝的 Channel 寫入後不阻塞(下一次寫入纔會阻塞),程序會繼續執行。

4. Channel 的數據結構

Channel 在 golang 中實際上就是個數據結構。在 Golang 源碼中, Channel 的數據結構 Hchan 的定義以下:

struct  Hchan
{
    uint32  qcount;         // total data in the q
    uint32  dataqsiz;       // size of the circular q
    uint16  elemsize;
    bool    closed;
    uint8   elemalign;
    Alg*    elemalg;        // interface for element type
    uint32  sendx;          // send index
    uint32  recvx;          // receive index
    WaitQ   recvq;          // list of recv waiters
    WaitQ   sendq;          // list of send waiters
    Lock;
};

時間倉促,此次對 Channel 介紹寫的有點簡單粗暴,下次再寫。

相關文章
相關標籤/搜索