Golang併發模型:select進階

最近公司工做有點多,Golang的select進階就這樣被拖沓啦,今天堅持把時間擠一擠,把吹的牛皮補上。golang

前一篇文章《Golang併發模型:輕鬆入門select》介紹了select的做用和它的基本用法,此次介紹它的3個進階特性。後端

  1. nil的通道永遠阻塞
  2. 如何跳出for-select
  3. select{}阻塞

nil的通道永遠阻塞

case上讀一個通道時,若是這個通道是nil,則該case永遠阻塞。這個功能有1個妙用,select一般處理的是多個通道,當某個讀通道關閉了,但不想select再繼續關注此case,繼續處理其餘case,把該通道設置爲nil便可。
下面是一個合併程序等待兩個輸入通道都關閉後才退出的例子,就使用了這個特性。緩存

func combine(inCh1, inCh2 <-chan int) <-chan int {
    // 輸出通道
    out := make(chan int)

    // 啓動協程合併數據
    go func() {
        defer close(out)
        for {
            select {
            case x, open := <-inCh1:
                if !open {
                    inCh1 = nil
                    continue
                }
                out<-x
            case x, open := <-inCh2:
                if !open {
                    inCh2 = nil
                    continue
                }
                out<-x
            }

            // 當ch1和ch2都關閉是才退出
            if inCh1 == nil && inCh2 == nil {
                break
            }
        }
    }()

    return out
}

如何跳出for-select

breakselect內的並不能跳出for-select循環。看下面的例子,consume函數從通道inCh不停讀數據,期待在inCh關閉後退出for-select循環,但結果是永遠沒有退出。bash

func consume(inCh <-chan int) {
    i := 0
    for {
        fmt.Printf("for: %d\n", i)
        select {
        case x, open := <-inCh:
            if !open {
                break
            }
            fmt.Printf("read: %d\n", x)
        }
        i++
    }

    fmt.Println("combine-routine exit")
}

運行結果:併發

➜ go run x.go
for: 0
read: 0
for: 1
read: 1
for: 2
read: 2
for: 3
gen exit
for: 4
for: 5
for: 6
for: 7
for: 8
... // never stop

既然break不能跳出for-select,那怎麼辦呢?給你3個錦囊:less

  1. 在知足條件的case內,使用return,若是有結尾工做,嘗試交給defer
  2. selectfor內使用break挑出循環,如combine函數。
  3. 使用goto

select{}永遠阻塞

select{}的效果等價於建立了1個通道,直接從通道讀數據:函數

ch := make(chan int)
<-ch

可是,這個寫起來多麻煩啊!沒select{}簡潔啊。
可是,永遠阻塞能有什麼用呢!?
當你開發一個併發程序的時候,main函數千萬不能在子協程幹完活前退出啊,否則全部的協程都被迫退出了,還怎麼提供服務呢?
好比,寫了個Web服務程序,端口監聽、後端處理等等都在子協程跑起來了,main函數這時候能退出嗎?spa

select應用場景

最後,介紹下我經常使用的select場景:code

  1. 無阻塞的讀、寫通道。即便通道是帶緩存的,也是存在阻塞的狀況,使用select能夠完美的解決阻塞讀寫,這篇文章我以前發在了我的博客,後面給你們介紹下。
  2. 給某個請求/處理/操做,設置超時時間,一旦超時時間內沒法完成,則中止處理。
  3. select本色:多通道處理

併發系列文章推薦

  1. Golang併發模型:輕鬆入門流水線模型
  2. Golang併發模型:輕鬆入門流水線FAN模式
  3. Golang併發模型:併發協程的優雅退出
  4. Golang併發模型:輕鬆入門select
  1. 若是這篇文章對你有幫助,請點個贊/喜歡,鼓勵我持續分享,感謝。
  2. 個人文章列表,點此可查看
  3. 若是喜歡本文,隨意轉載,但請保留此原文連接

一塊兒學Golang-分享有料的Go語言技術

相關文章
相關標籤/搜索