Go語言並發的設計模式和應用情境

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

產生器

  在Python中我們可以使用yield關鍵字來讓一個函數成為產生器,在Go中我們可以使用通道來製造產生器(一種lazy load類似的東西)。

  當然我們的通道並不是簡單的做阻塞主線的功能來使用的哦。

  下面是一個製作自增整數產生器的例子,直到主線向通道索要資料,我們才添加資料到通道:

func xrange() chan int{ // xrange用來產生自增的整數    var ch chan int = make(chan int)    go func() { // 開出一個goroutine        for i := 0; ; i++ {            ch <- i  // 直到通道索要資料,才把i添加進通道        }    }()    return ch}func main() {    generator := xrange()    for i:=0; i < 1000; i++ {  // 我們產生1000個自增的整數!        fmt.Println(<-generator)    }}

這不禁叫我想起了Python中可愛的xrange, 所以給了產生器這個名字!

服務化

比如我們載入一個網站的時候,例如我們登入新浪微博,我們的訊息資料應該來自一個獨立的服務,這個服務只負責 返回某個使用者的新的訊息提醒。

如下是一個使用樣本:

func get_notification(user string) chan string{   /*    * 此處可以查詢資料庫擷取新訊息等等..    */    notifications := make(chan string)    go func() { // 懸掛一個通道出去        notifications <- fmt.Sprintf("Hi %s, welcome to weibo.com!", user)    }()    return notifications}func main() {    jack := get_notification("jack") //  擷取jack的訊息    joe := get_notification("joe") // 擷取joe的訊息    // 擷取訊息的返回    fmt.Println(<-jack)    fmt.Println(<-joe)}

多路複合

上面的例子都使用一個通道作為傳回值,可以把通道的資料合併到一個通道的。 不過這樣的話,我們需要按順序輸出我們的傳回值(先進先出)。

如下,我們假設要計算很複雜的一個運算 100-x , 分為三路計算, 最後統一在一個通道中取出結果:

func do_stuff(x int) int { // 一個比較耗時的事情,比如計算    time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) //類比計算    return 100 - x // 假如100-x是一個很費時的計算}func branch(x int) chan int{ // 每個分支開出一個goroutine做計算並把計算結果流入各自通道    ch := make(chan int)    go func() {        ch <- do_stuff(x)    }()    return ch}func fanIn(chs... chan int) chan int {    ch := make(chan int)    for _, c := range chs {        // 注意此處明確傳值        go func(c chan int) {ch <- <- c}(c) // 複合    }    return ch}func main() {    result := fanIn(branch(1), branch(2), branch(3))    for i := 0; i < 3; i++ {        fmt.Println(<-result)    }}

select監聽通道

Go有一個語句叫做select,用於監測各個通道的資料流動。

如下的程式是select的一個使用例子,我們監視三個通道的資料流出並收集資料到一個通道中。

func foo(i int) chan int {    c := make(chan int)    go func () { c <- i }()    return c}func main() {    c1, c2, c3 := foo(1), foo(2), foo(3)    c := make(chan int)    go func() { // 開一個goroutine監視各個通道資料輸出並收集資料到通道c        for {            select { // 監視c1, c2, c3的流出,並全部流入通道c            case v1 := <- c1: c <- v1            case v2 := <- c2: c <- v2            case v3 := <- c3: c <- v3            }        }    }()    // 阻塞主線,取出通道c的資料    for i := 0; i < 3; i++ {        fmt.Println(<-c) // 從列印來看我們的資料輸出並不是嚴格的1,2,3順序    }}

有了select, 我們在 多路複合中的範例程式碼中的函數fanIn還可以這麼來寫(這樣就不用開好幾個goroutine來取資料了):

func fanIn(branches ... chan int) chan int {    c := make(chan int)    go func() {        for i := 0 ; i < len(branches); i++ { //select會嘗試著依次取出各個通道的資料            select {            case v1 := <- branches[i]: c <- v1            }        }    }()    return c}

使用select的時候,有時需要逾時處理, 其中的timeout通道相當有趣:

timeout := time.After(1 * time.Second) // timeout 是一個計時通道, 如果達到時間了,就會發一個訊號出來for is_timeout := false; !is_timeout; {    select { // 監視通道c1, c2, c3, timeout通道的資料流出    case v1 := <- c1: fmt.Printf("received %d from c1", v1)    case v2 := <- c2: fmt.Printf("received %d from c2", v2)    case v3 := <- c3: fmt.Printf("received %d from c3", v3)    case <- timeout: is_timeout = true // 逾時    }}

結束標誌

在Go並發與並行筆記一我們已經講過通道的一個很重要也很平常的應用,就是使用無緩衝通道來阻塞主線,等待goroutine結束。

這樣我們不必再使用timeout。

那麼對上面的timeout來結束主線的方案作個更新:

func main() {    c, quit := make(chan int), make(chan int)    go func() {        c <- 2  // 添加資料        quit <- 1 // 發送完成訊號    } ()    for is_quit := false; !is_quit; {        select { // 監視通道c的資料流出        case v := <-c: fmt.Printf("received %d from c", v)        case <-quit: is_quit = true // quit通道有輸出,關閉for迴圈        }    }}

菊花鏈

簡單地來說,資料從一端流入,從另一端流出,看上去好像一個鏈表,不知道為什麼要取這麼個尷尬的名字。。

菊花鏈的英文名字叫做: Daisy-chain, 它的一個應用就是做過濾器,比如我們來篩下100以內的素數(你需要Crowdsourced Security Testing道什麼是篩法)

程式有詳細的注釋,不再說明了。

/* *  利用通道菊花鏈篩法求某一個整數範圍的素數 *  篩法求素數的基本思想是:把從1開始的、某一範圍內的正整數從小到大順序排列, *  1不是素數,首先把它篩掉。剩下的數中選擇最小的數是素數,然後去掉它的倍數。 *  依次類推,直到篩子為空白時結束 */package mainimport "fmt"func xrange() chan int{ // 從2開始自增的整數產生器    var ch chan int = make(chan int)    go func() { // 開出一個goroutine        for i := 2; ; i++ {            ch <- i  // 直到通道索要資料,才把i添加進通道        }    }()    return ch}func filter(in chan int, number int) chan int {    // 輸入一個整數隊列,篩出是number倍數的, 不是number的倍數的放入輸出隊列    // in:  輸入隊列    out := make(chan int)    go func() {        for {            i := <- in // 從輸入中取一個            if i % number != 0 {                out <- i // 放入輸出通道            }        }    }()    return out}func main() {    const max = 100 // 找出100以內的所有素數    nums := xrange() // 初始化一個整數產生器    number := <-nums  // 從產生器中抓一個整數(2), 作為初始化整數    for number <= max { // number作為篩子,當篩子超過max的時候結束篩選        fmt.Println(number) // 列印素數, 篩子即一個素數        nums = filter(nums, number) //篩掉number的倍數        number = <- nums  // 更新篩子    }}

隨機數產生器

通道可以做產生器使用,作為一個特殊的例子,它還可以用作隨機數產生器。如下是一個隨機01產生器:

func rand01() chan int {    ch := make(chan int)    go func () {        for {            select { //select會嘗試執行各個case, 如果都可以執行,那麼隨機選一個執行            case ch <- 0:            case ch <- 1:            }        }    }()    return ch}func main() {    generator := rand01() //初始化一個01隨機產生器    //測試,列印10個隨機01    for i := 0; i < 10; i++ {        fmt.Println(<-generator)    }}

定時器

我們剛才其實已經接觸了通道作為定時器, time包裡的After會製作一個定時器。

看看我們的定時器吧!

/* * 利用通道做定時器 */package mainimport (    "fmt"    "time")func timer(duration time.Duration) chan bool {    ch := make(chan bool)    go func() {        time.Sleep(duration)        ch <- true // 到時間啦!    }()    return ch}func main() {    timeout := timer(time.Second) // 定時1s    for {        select {        case <- timeout:            fmt.Println("already 1s!") // 到時間            return  //結束程式        }    }}

TODO

Google的應用情境例子。

聯繫我們

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