【golang】select關鍵字用法

select是go語言中常用的一個關鍵字,其用法也一直被用作面試題來考核應聘者。今天,結合代碼來分析下select的主要用法。

首先,我們來從官方文檔看一下有關select的描述:

A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.
一個select語句用來選擇哪個case中的發送或接收操作可以被立即執行。它類似于switch語句,但是它的case涉及到channel有關的I/O操作。

或者換一種說法,select就是用來監聽和channel有關的IO操作,當 IO 操作發生時,觸發相應的動作。

基本用法

//select基本用法
select {
case <- chan1:
// 如果chan1成功讀到數據,則進行該case處理語句
case chan2 <- 1:
// 如果成功向chan2寫入數據,則進行該case處理語句
default:
// 如果上面都沒有成功,則進入default處理流程

官方執行步驟

Execution of a "select" statement proceeds in several steps:

1.For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.
所有channel表達式都會被求值、所有被發送的表達式都會被求值。求值順序:自上而下、從左到右.
結果是選擇一個發送或接收的channel,無論選擇哪一個case進行操作,表達式都會被執行。RecvStmt左側短變量聲明或賦值未被評估。

  1. If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
    如果有一個或多個IO操作可以完成,則Go運行時系統會隨機的選擇一個執行,否則的話,如果有default分支,則執行default分支語句,如果連default都沒有,則select語句會一直阻塞,直到至少有一個IO操作可以進行.

3.Unless the selected case is the default case, the respective communication operation is executed.
除非所選擇的情況是默認情況,否則執行相應的通信操作。

4.If the selected case is a RecvStmt with a short variable declaration or an assignment, the left-hand side expressions are evaluated and the received value (or values) are assigned.
如果所選case是具有短變量聲明或賦值的RecvStmt,則評估左側表達式并分配接收值(或多個值)。

5.The statement list of the selected case is executed.
執行所選case中的語句

案例分析

案例1 如果有一個或多個IO操作可以完成,則Go運行時系統會隨機的選擇一個執行,否則的話,如果有default分支,則執行default分支語句,如果連default都沒有,則select語句會一直阻塞,直到至少有一個IO操作可以進行
start := time.Now()
    c := make(chan interface{})
    ch1 := make(chan int)
        ch2 := make(chan int)

    go func() {

        time.Sleep(4*time.Second)
        close(c)
    }()

    go func() {

        time.Sleep(3*time.Second)
        ch1 <- 3
    }()

      go func() {

        time.Sleep(3*time.Second)
        ch2 <- 5
    }()

    fmt.Println("Blocking on read...")
    select {
    case <- c:

        fmt.Printf("Unblocked %v later.\n", time.Since(start))

    case <- ch1:

        fmt.Printf("ch1 case...")
      case <- ch2:

        fmt.Printf("ch1 case...")
    default:

        fmt.Printf("default go...")
    }

運行上述代碼,由于當前時間還未到3s。所以,目前程序會走default。

Blocking on read...
default go...
Process finished with exit code 0

修改代碼,將default注釋:

//default:
 //       fmt.Printf("default go...")

這時,select語句會阻塞,直到監測到一個可以執行的IO操作為止。這里,先會執行完睡眠3s的gorountine,此時兩個channel都滿足條件,這時系統會隨機選擇一個case繼續操作。

Blocking on read...
ch2 case...
Process finished with exit code 0

接著,繼續修改代碼,將ch的gorountine休眠時間改為5s:

go func() {

        time.Sleep(5*time.Second)
        ch1 <- 3
    }()
go func() {

        time.Sleep(5*time.Second)
        ch2 <- 3
    }()

此時會先執行到上面的gorountine,select執行的就是c的case。

Blocking on read...
Unblocked 4.000612584s later.
Process finished with exit code 0
示例2 所有channel表達式都會被求值、所有被發送的表達式都會被求值。求值順序:自上而下、從左到右.
var ch1 chan int
var ch2 chan int
var chs = []chan int{ch1, ch2}
var numbers = []int{1, 2, 3, 4, 5}

func main () {

    select {
    case getChan(0) <- getNumber(2):

        fmt.Println("1th case is selected.")
    case getChan(1) <- getNumber(3):

        fmt.Println("2th case is selected.")
    default:

        fmt.Println("default!.")
        }
        }

func getNumber(i int) int {
    fmt.Printf("numbers[%d]\n", i)

    return numbers[i]
}
func getChan(i int) chan int {
    fmt.Printf("chs[%d]\n", i)

    return chs[i]
}

此時,select語句走的是default操作。但是這時每個case的表達式都會被執行。以case1為例:

case getChan(0) <- getNumber(2):

系統會從左到右先執行getChan函數打印chs[0],然后執行getNumber函數打印numbers[2]。同樣,從上到下分別執行所有case的語句。所以,程序執行的結果為:

chs[0]
numbers[2]
chs[1]
numbers[3]
default!.

Process finished with exit code 0
示例3 break關鍵字結束select
ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)

    ch1 <- 3
    ch2 <- 5

    select {
    case <- ch1:

        fmt.Println("ch1 selected.")

        break

        fmt.Println("ch1 selected after break")
    case <- ch2:

        fmt.Println("ch2 selected.")
        fmt.Println("ch2 selected without break")
    }

很明顯,ch1和ch2兩個通道都可以讀取到值,所以系統會隨機選擇一個case執行。我們發現選擇執行ch1的case時,由于有break關鍵字只執行了一句:

ch1 selected.

Process finished with exit code 0

但是,當系統選擇ch2的case時,打印結果為:

ch2 selected.
ch2 selected without break

Process finished with exit code 0

如此就顯而易見,break關鍵字在select中的作用。

.
.
.
.

互聯網顛覆世界,區塊鏈顛覆互聯網!

--------------------------------------------------20180908 22:46
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容