Goroutines和Channels(五)

Channels也能夠用於將多個goroutine鏈接在一塊兒,一個Channel的輸出做爲下一個Channel的輸入。這種串聯的Channels就是所謂的管道(pipeline)。下面的程序用兩個channels將三個goroutine串聯起來:函數

第一個goroutine是一個計數器,用於生成0、一、二、……形式的整數序列,而後經過channel將該整數序列發送給第二個goroutine;第二個goroutine是一個求平方的程序,對收到的每一個整數求平方,而後將平方後的結果經過第二個channel發送給第三個goroutine;第三個goroutine是一個打印程序,打印收到的每一個整數。爲了保持例子清晰,咱們有意選擇了很是簡單的函數,固然三個goroutine的計算很簡單,在現實中確實沒有必要爲如此簡單的運算構建三個goroutine。測試

 

func main() {
    naturals := make(chan int)
    squares := make(chan int)

    // Counter
    go func() {
        for x := 0; ; x++ {
            naturals <- x
        }
    }()

    // Squarer
    go func() {
        for {
            x := <-naturals
            squares <- x * x
        }
    }()

    // Printer (in main goroutine)
    for {
        fmt.Println(<-squares)
    }
}

  

如您所料,上面的程序將生成0、一、四、九、……形式的無窮數列。像這樣的串聯Channels的管道(Pipelines)能夠用在須要長時間運行的服務中,每一個長時間運行的goroutine可能會包含一個死循環,在不一樣goroutine的死循環內部使用串聯的Channels來通訊。可是,若是咱們但願經過Channels只發送有限的數列該如何處理呢?blog

若是發送者知道,沒有更多的值須要發送到channel的話,那麼讓接收者也能及時知道沒有多餘的值可接收將是有用的,由於接收者能夠中止沒必要要的接收等待。這能夠經過內置的close函數來關閉channel實現:ip

 

close(naturals)

  

當一個channel被關閉後,再向該channel發送數據將致使panic異常。當一個被關閉的channel中已經發送的數據都被成功接收後,後續的接收操做將再也不阻塞,它們會當即返回一個零值。pip

關閉上面例子中的naturals變量對應的channel並不能終止循環,它依然會收到一個永無休止的零值序列,而後將它們發送給打印者goroutine。io

 

沒有辦法直接測試一個channel是否被關閉,可是接收操做有一個變體形式:它多接收一個結果,多接收的第二個結果是一個布爾值ok,ture表示成功從channels接收到值,false表示channels已經被關閉而且裏面沒有值可接收。使用這個特性,咱們能夠修改squarer函數中的循環代碼,當naturals對應的channel被關閉並無值可接收時跳出循環,而且也關閉squares對應的channel.class

 

// Squarer
go func() {
    for {
        x, ok := <-naturals
        if !ok {
            break // channel was closed and drained
        }
        squares <- x * x
    }
    close(squares)
}()

  

由於上面的語法是笨拙的,並且這種處理模式很常見,所以Go語言的range循環可直接在channels上面迭代。使用range循環是上面處理模式的簡潔語法,它依次從channel接收數據,當channel被關閉而且沒有值可接收時跳出循環。變量

在下面的改進中,咱們的計數器goroutine只生成100個含數字的序列,而後關閉naturals對應的channel,這將致使計算平方數的squarer對應的goroutine能夠正常終止循環並關閉squares對應的channel。(在一個更復雜的程序中,能夠經過defer語句關閉對應的channel。)最後,主goroutine也能夠正常終止循環並退出程序。sed

 

func main() {
    naturals := make(chan int)
    squares := make(chan int)

    // Counter
    go func() {
        for x := 0; x < 100; x++ {
            naturals <- x
        }
        close(naturals)
    }()

    // Squarer
    go func() {
        for x := range naturals {
            squares <- x * x
        }
        close(squares)
    }()

    // Printer (in main goroutine)
    for x := range squares {
        fmt.Println(x)
    }
}

  

其實你並不須要關閉每個channel。只有當須要告訴接收者goroutine,全部的數據已經所有發送時才須要關閉channel。無論一個channel是否被關閉,當它沒有被引用時將會被Go語言的垃圾自動回收器回收。(不要將關閉一個打開文件的操做和關閉一個channel操做混淆。對於每一個打開的文件,都須要在不使用的時候調用對應的Close方法來關閉文件。)循環

試圖重複關閉一個channel將致使panic異常,試圖關閉一個nil值的channel也將致使panic異常。關閉一個channels還會觸發一個廣播機制。

相關文章
相關標籤/搜索