【go語言】Goroutines 併發模式(二)

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

前言

在Goroutines 併發模式(一)中,我們簡單地通過boring函數的例子來粗略地闡述了通過channels來和goroutines交流的方法。在本篇中,我將從pattern的方向出發,通過對boring函數的例子進行各種改寫,來講解幾種常見了goroutines的併發模式。


併發模式

讓我們先來回顧一下boring函數的例子。

func boring(msg string, c chan string) {   for i := 0; ; i++ {        c <- fmt.Sprintf("%s %d", msg, i)        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)}}           func main() {c := make(chan string)go boring("boring!", c) for i := 0; i < 5; i++ {    fmt.Printf("You say: %q\n", <-c)    }    fmt.Println("You're boring; I'm leaving.")}

接下來,我會base於上面的這個例子,來介紹各種patterns。

  • 產生器(Generator)

由於go中的channel也是一種變數,所以我們可以通過返回channel的方式來傳遞結果

func boring(msg string) <-chan string {     c := make(chan string)    go func() {         for i := 0; ; i++ {            c <- fmt.Sprintf("%s %d", msg, i)            time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)        }    }()    return c }func main(){c := boring("boring!")     for i := 0; i < 5; i++ {        fmt.Printf("You say: %q\n", <-c)    }    fmt.Println("You're boring; I'm leaving.")}

通過這個例子,我們可以很容易想到其他運用返回結果channel的例子,這樣做不僅使得程式更加的清晰,而且更加有利於的非阻塞過程的組織,因為我們可以在任何必要的時候通過結果channel讀取結果。如此一來,我們可以將boring作為一種服務,就像下面的例子:

func main() {    joe := boring("Joe")    ann := boring("Ann")    for i := 0; i < 5; i++ {        fmt.Println(<-joe)        fmt.Println(<-ann)    }    fmt.Println("You're both boring; I'm leaving.")}
  • 多路複合(Multiplexing)

func fanIn(input1, input2 <-chan string) <-chan string {    c := make(chan string)    go func() { for { c <- <-input1 } }()    go func() { for { c <- <-input2 } }()    return c}func main() {    c := fanIn(boring("Joe"), boring("Ann"))    for i := 0; i < 10; i++ {        fmt.Println(<-c)    }    fmt.Println("You're both boring; I'm leaving.")}

我們通過fanIn函數將兩個boring函數返回的結果channel給複合到了一個channel中,這樣我們可以看到在main函數中通過複合後的channel讀出的結果資料將是隨機的。下面這張圖很形象地的展現了多路複合模式的過程。


  • 選擇(Select)

Go中的select其實和Unix/Linux下的多工select在思想上有異曲同工之妙,我們可以通過Select來做很多很美妙的事情。首先,我們來改寫fanin方法,把它改寫為使用select的版本:

func fanIn(input1, input2 <-chan string) <-chan string {    c := make(chan string)    go func() {        for {            select {            case s := <-input1:  c <- s            case s := <-input2:  c <- s            }        }    }()    return c}

這裡的select將同時監聽多個channel,只要有其中一個channel可以讀取資料,那麼select就將解除阻塞狀態,運行相應case下的代碼。如果您寫過一些高效能的並發程式,那麼您一定早就發現select真乃神器,select不僅可以簡化代碼清晰邏輯,而且可以減少IO並發開銷,大大增大並發輸送量。

  • 逾時(Timeout)

在goroutines中,有時候可能會因為等待某個channel而長期阻塞某個goroutine,所以我們需要為之增加逾時的功能。下面例子將使用select實現逾時功能。

func main() {    c := boring("Joe")    for {        select {        case s := <-c:            fmt.Println(s)        case <-time.After(1 * time.Second):            fmt.Println("You're too slow.")            return        }    }}

這裡的time是go提供的一個庫,After方法將返回一個在相應時間之後可以讀取的channel,這樣我們使用select就可以很方便得實現逾時處理的功能。

  • 退出

那麼我們怎麼來控制一個goroutine,使它可以結束自己的使命正常結束呢?其實很簡單,同樣我們使用select來實現這個功能。

func boring(msg string, quit chan bool) <-chan string {     c := make(chan string)    go func() {         for i := 0; ; i++ {        select {        case c <- fmt.Sprintf("%s: %d", msg, i):        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)        case <-quit:        return        }        }    }()    return c}func main(){quit := make(chan bool)    c := boring("Joe", quit)    for i := rand.Intn(10); i >= 0; i-- { fmt.Println(<-c) }    quit <- true}

通過在boring的迴圈中增加一個select,在main中我們便可以通過向quit 寫入資料的方式來控制boring的退出。換句話來講,其實就是做到了不同goroutines間的一個交流罷了。

  • 菊花鏈(Daisy-chain)

要說清楚什麼是菊花鏈,讓我們先看一幅圖

我們看圖說話,圖中的gopher是一個一個channel,這些channel從頭到尾連了起來。但我們把一個資料放到channel的頭部的時候,通過傳遞,我們便可以從channel的尾部讀出資料。是不是覺得這很像大家小時候玩的傳悄悄話的遊戲??具體執行個體如下:

func f(left, right chan int) {    left <- 1 + <-right}func main() {    const n = 100000    leftmost := make(chan int)    right := leftmost    left := leftmost    for i := 0; i < n; i++ {        right = make(chan int)        go f(left, right)        left = right    }    go func(c chan int) { c <- 1 }(right)    fmt.Println(<-leftmost)}

上面代碼初始化了100000個channel,並把他們按照順序串連起來。最後向最右邊的channel寫入一個資料,從最左邊的channel讀出來。這種菊花鏈的模型非常適合作為過濾器filter來使用,通過channel來串連filter會顯得十分方便。


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.