Golang併發模型:select進階

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

前一篇文章《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循環,但結果是永遠沒有退出。

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個錦囊:

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

select{}永遠阻塞

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

ch := make(chan int)
<-ch

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

select應用場景

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

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

併發系列文章推薦

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章