Go語言並行存取模型:使用 select

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

簡介

作為一種現代語言,go語言實現了對並發的原生支援。上幾期文章中,我們對goroutine 和 channel進行了詳細的講解。但是要實現對 channel 的控制,從語言層面上來說,select 語句是必不可少的部分。本文中,我們就 select 語句的行為和使用方法進行深入討論。

閱讀建議

本文中的內容是 Go語言並行存取模型的一篇,但是與上幾期關係不是特別密切,可以獨立閱讀。本文的內容來源自於 go language specifications 和 Rob Pike 在2012年進行的一場名為"concurrency" 的演講。如果有時間的話,建議在 YouTube 上看一下他本人的演講。

select 語句的行為

為了便於理解,我們首先給出一個程式碼片段:

// https://talks.golang.org/2012/concurrency.slide#32select {case v1 := <-c1:    fmt.Printf("received %v from c1\n", v1)case v2 := <-c2:    fmt.Printf("received %v from c2\n", v1)case c3 <- 23:    fmt.Printf("sent %v to c3\n", 23)default:    fmt.Printf("no one was ready to communicate\n")}

上面這段代碼中,select 語句有四個 case 子語句,前兩個是 receive 操作,第三個是 send 操作,最後一個是預設操作。代碼執行到 select 時,case 語句會按照原始碼的順序被評估,且只評估一次,評估的結果會出現下面這幾種情況:

  1. 除 default 外,如果只有一個 case 語句評估通過,那麼就執行這個case裡的語句;

  2. 除 default 外,如果有多個 case 語句評估通過,那麼通過偽隨機的方式隨機選一個;

  3. 如果 default 外的 case 語句都沒有通過評估,那麼執行 default 裡的語句;

  4. 如果沒有 default,那麼 代碼塊會被阻塞,指導有一個 case 通過評估;否則一直阻塞

如果 case 語句中 的 receive 操作的對象是 nil channel,那麼也會阻塞,下面我們看一個更全面、用法也更進階的例子:

// https://golang.org/ref/spec#Select_statementsvar a []intvar c, c1, c2, c3, c4 chan intvar i1, i2 intselect {case i1 = <-c1:    print("received ", i1, " from c1\n")case c2 <- i2:    print("sent ", i2, " to c2\n")case i3, ok := (<-c3):  // same as: i3, ok := <-c3    if ok {        print("received ", i3, " from c3\n")    } else {        print("c3 is closed\n")    }case a[f()] = <-c4:    // same as:    // case t := <-c4    //    a[f()] = tdefault:    print("no communication\n")}for {  // 向 channel c 發送隨機 bit 串    select {    case c <- 0:  // note: no statement, no fallthrough, no folding of cases    case c <- 1:    }}select {}  // 永久阻塞

注意:與 C/C++ 等傳統程式設計語言不同,go語言的 case 語句不需要 break 關鍵字去跳出 select。

select 的使用

為請求設定逾時時間

在 golang 1.7 之前, http 包並沒有引入 context 支援,通過 http.Client 向一個壞掉的服務發送請求會導致響應緩慢。類似的情境下,我們可以使用 select 控制服務回應時間,下面是一個簡單的demo:

func main() {    c := boring("Joe")    timeout := time.After(5 * time.Second)    for {        select {        case s := <-c:            fmt.Println(s)        case <-timeout:            fmt.Println("You talk too much.")            return        }    }}

done channel

上幾期的文章中,我們均討論過 done channel,它可以用於保證流水線上每個階段goroutine 的退出。在 golang.org/x/net 包中,done channel 被廣泛應用。這裡我們看一眼 golang.org/x/net/context/ctxhttp 中 Do 方法的實現:

// https://github.com/golang/net/blob/release-branch.go1.7/context/ctxhttp/ctxhttp.go// Do sends an HTTP request with the provided http.Client and returns// an HTTP response.//// If the client is nil, http.DefaultClient is used.//// The provided ctx must be non-nil. If it is canceled or times out,// ctx.Err() will be returned.func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {    if client == nil {        client = http.DefaultClient    }    resp, err := client.Do(req.WithContext(ctx))    // If we got an error, and the context has been canceled,    // the context's error is probably more useful.    if err != nil {        select {        case <-ctx.Done():            err = ctx.Err()        default:        }    }    return resp, err} 

quit channel

在很多情境下,quit channel 和 done channel 是一個概念。在並發程式中,通常 main routine 將任務分給其它 go routine 去完成,而自身只是起到調度作用。這種情況下,main 函數無法知道 其它goroutine 任務是否完成,此時我們需要 quit channel;對於更細粒度的控制,比如完成多少,還是需要 done channel (參考WaitGroup)。 下面是 quit channel 的一個例子,首先是 main routine:

// 建立 quit channelquit := make(chan string)// 啟動生產者 goroutinec := boring("Joe", quit)// 從生產者 channel 讀取結果for i := rand.Intn(10); i >= 0; i-- { fmt.Println(<-c) }// 通過 quit channel 通知生產者停止生產quit <- "Bye!"fmt.Printf("Joe says: %q\n", <-quit)

我們再看 生產者 go routine 中與 quit channel 相關的部分:

select {case c <- fmt.Sprintf("%s: %d", msg, i):    // do nothingcase <-quit:    cleanup()    quit <- "See you!"    return}

Google Search (延伸閱讀)

Google Search 是一個很經典的例子,由於代碼較多,有興趣的童鞋查看 Rob Pike 的 ppt。
更高階的並發方式可以閱讀 Sameer Ajmani 的 ppt Advanced Go Concurrency Patterns

並發相關的主題就先到這裡,下一期文章中,我們會討論go語言測試載入器鏈中的單元測試。

相關連結:

  1. Rob Pike演講:concurrency

  2. language specification: select statement

掃碼關注公眾號“深入Go語言”

相關文章

聯繫我們

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