圖解 Go Select 語句的執行順序

2021-02-22 Go語言中文網

本文基於 Go 1.14

select 允許在一個 goroutine 中管理多個 channel。但是,當所有 channel 同時就緒的時候,go 需要在其中選擇一個執行。此外,go 還需要處理沒有 channel 就緒的情況,我們先從就緒的 channel 開始。

順序

select 不會按照任何規則或者優先級選擇就緒的 channel。go 標準庫在每次執行的時候,都會將他們順序打亂,也就是說不能保證任何順序。

看一個有三個就緒的 channel 的例子:

func main() {
 a := make(chan bool, 100)
 b := make(chan bool, 100)
 c := make(chan bool, 100)
 for i := 0; i < 10; i++ {
  a <- true
  b <- true
  c <- true
 }
 for i := 0; i < 10; i++ {
  select {
  case <-a:
   print("< a")

  case <-b:
   print("< b")

  case <-c:
   print("< c")

  default:
   print("< default")
  }
 }
}

這三個 channel 的緩衝區都填滿了,使得 select 選擇時不會堵塞。下面是程序的輸出:

< b< a< a< b< c< c< c< a< b< b

在 select 的每次迭代中,case 都會被打亂:

由於 go 不會刪除重複的 channel,所以可以使用多次添加 case 來影響結果,代碼如下:

func main() {
 a := make(chan bool, 100)
 b := make(chan bool, 100)
 c := make(chan bool, 100)
 for i := 0; i < 10; i++ {
  a <- true
  b <- true
  c <- true
 }
 for i := 0; i < 10; i++ {
  select {
  case <-a:
   print("< a")
  case <-a:
   print("< a")
  case <-a:
   print("< a")
  case <-a:
   print("< a")
  case <-a:
   print("< a")
  case <-a:
   print("< a")
  case <-a:
   print("< a")

  case <-b:
   print("< b")

  case <-c:
   print("< c")

  default:
   print("< default")
  }
 }
}

輸出的結果:

< c< a< b< a< b< a< a< c< a< a

當所有 channel 同時準備就緒時,有 80%的機會選擇通道 a。下面來看一下 channel 未就緒的情況。

沒有就緒 channels

select 運行時,如果沒有一個 case channel 就緒,那麼他就會運行 default:,如果 select 中沒有寫 default,那麼他就進入等待狀態,如下面這個例子

func main() {
 a := make(chan bool, 100)
 b := make(chan bool, 100)
 Go func() {
  time.Sleep(time.Minute)
  for i := 0; i < 10; i++ {
   a <- true
   b <- true
  }
 }()

 for i := 0; i < 10; i++ {
  select {
  case <-a:
   print("< a")
  case <-b:
   print("< b")
  }
 }
}

上面那個例子中,將在一分鐘後列印結果。select 阻塞在 channel 上。這種情況下,處理 select 的函數將會訂閱所有 channel 並且等待,下面是一個 goroutine#7 在 select 中等待的示例,其中另一個 goroutine#4 也在等待 channel:

Goroutine(G7)訂閱所有頻道並在列表末尾等待。如果 channel 發送了一條消息,channel 將通知已在等待該消息的另一個 Goroutine。一旦收到通知,select 將取消訂閱所有 channel,並且返回到代碼運行.

更多關於 channel 與等待隊列的信息,請查看作者另外一篇文章*Go: 帶緩衝和不帶緩衝的 Channels*[1]。

上面介紹的邏輯,都是針對於有兩個或者以上的活動的 channel,實際上如果只有一個活動的 channel,Go 樂意簡化 select。

簡化

如果只有一個 case 加上一個 default,例子:

func main() {
 t:= time.NewTicker(time.Second)
 for   {
  select {
  case <-t.C:
   print("1 second ")
  default:
   print("default branch")
  }
 }
}

這種情況下。Go 會以非阻塞模式讀取 channel 的操作替換 select 語句。如果 channel 在緩衝區中沒有任何值,或者發送方準備發送消息,將會運行 default。就像下面這張圖:

如果沒有 default,則 Go 通過阻塞 channel 的操作方式重寫 select 語句。

via: https://medium.com/a-journey-with-go/go-ordering-in-select-statements-fd0ff80fd8d6

作者:Vincent Blanchon[2]譯者:yixiao9206[3]校對:polaris1119[4]

本文由 GCTT[5] 原創編譯,Go 中文網[6] 榮譽推出

參考資料[1]

Go: 帶緩衝和不帶緩衝的 Channels: https://studygolang.com/articles/23538

[2]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[3]

yixiao9206: https://github.com/yixiao9206

[4]

polaris1119: https://github.com/polaris1119

[5]

GCTT: https://github.com/studygolang/GCTT

[6]

Go 中文網: https://studygolang.com/

相關焦點

  • 用Select Case語句對執行多條件進行控制
    第三節 用Select Case語句對執行多條件進行控制本章講的是條件語句的判斷與執行,利用If Then Else語句,雖然可以對多條件進行判斷執行操作,但用起來非常的不方便,我們這節介紹給大家一種Select Case語句,用這種語句處理多條件的執行操作會更加方便。
  • MySQL的SQL語句 - 數據操作語句(7)- INSERT SELECT 語句
    SELECT 語句,可以從 SELECT 語句的結果中快速地將許多行插入到表中,該語句可以從一個或多個表中進行選擇。不帶 ORDER BY 子句的 SELECT 或 TABLE 語句返回行的順序是不確定的。這意味著,在使用複製時,不能保證這樣的 SELECT 在主伺服器和從伺服器上以相同的順序返回行,這可能會導致它們之間的不一致。要防止這種情況發生,請確保用於複製的 INSERT ... SELECT 或 INSERT ...
  • 關於DQL查詢語句(還有優先度排行)
    簡單的查詢語句DQL語法格式:select 欄位名1,欄位名2 from 表名;
  • Excel VBA流程圖解之Select Case語句 多分支選擇的最佳選擇
    上一篇,我們畫了一個IF的流程圖,顯然有些場景用IF寫起來比較繁瑣,比如常用與分數段判斷,時間段,稅率,提成比例等問題,IF每個條件裡都要寫判斷的對象,很是麻煩,那麼Select Case就是為此而生,Seelct Case語句只需要寫一個判斷的對象
  • 【VBA語法篇】第二課:VBA語句之select判斷語句使用
    本節課主要講解語法篇的第二課,VBA當中的select判斷語句的使用。一、select語句單一條件判斷:Sub select單條件判斷()i = 1Select Case iCase Is > 0    MsgBox "正數"Case Else    MsgBox "負數"End SelectEnd Sub
  • Python VS MySQL,我發現了select和print之間的「相通點」!
    那就是對比Python中的print後,你對於MySQL中的select關鍵字,會有一個更深刻的認識,這其實是黃同學突發奇想的一個選題,對一部分學習MySQL的同學來說 ,還是很有幫助的。這個對於你學習MySQL也是很有幫助的。
  • C語言while語句
    在C語言中,共有三大常用的程序結構:前面講解了順序結構和選擇結構,本節開始講解循環結構。
  • MySQL中order by語句的實現原理以及優化手段
    對應的 SQL 語句應該這麼寫:select id_card,name,age from user where name = '張三' order by age limit 3;這條 SQL 語句邏輯比較簡單,很好理解,語句在執行時會使用到 name 索引樹,並且會進行排序
  • 【怎麼辦】圖解式學習法9步驟
    精讀:Jingdu999每天一個小微課程。【一】9步圖解學習法1、圍繞關鍵詞來連接不管是什麼樣的學習,圖解的第一步工作都是尋找關鍵詞。③在文具店,花了5元買了一隻原子筆(商品/服務)。這種用金錢與商品/服務進行等價交換的買賣形式稱為「交易模式」。3、用線表示「對等關係」、「敵對關係」與前面所說的箭頭表示不同,兩者的關係對等,沒有先後順序,適合用無箭頭的線連接。
  • 用Select Case語句對分段稅率進行精確計算
    今日的內容是「VBA之EXCEL應用」的第六章「條件判斷語句(If...Then...Else)在VBA中的利用」。
  • VBA中的If-Then-Else語句
    可以是由冒號分隔的一個或多個語句;在 condition 為 True 時執行。③ condition-n   可選。 與condition相同。④ elseifstatements   可選。 如果關聯condition-n為True, 則執行一個或多個語句。⑤ elsestatements   可選。
  • Go語言學習Tips(一)
    對於nested同一個channel select,如何想要層層返回,最好的方式就是直接close channel :package mainimport (     "fmt"     "time")func main() {     stopc := make(chan int)
  • switch 語句
    (3) switch 下面的花括號內是一個語句塊。語句塊中包含多個以 case 開頭、break 結尾的行、最多一個以 default 開頭的行出現在語句塊的結束部分,並且只能出現一次。(4) 當表達式的值與 case 後面的值相等時,就執行該 case 後面的語句,接著執行下一行的break,然後退出整個switch語句,switch中其他語句都不再執行,這一點和if…else if…else語句有點類似。(5) 如果表達式的值與所有 case 後面的值都不匹配時,則執行 default 後面的語句,然後退出 switch。
  • SQL 語句中 where 條件後 寫上1=1 是什麼意思
    前言where 1=1是sql語句條件邏輯判斷表達式,由於1=1成立,恆為真,該表達式1=1將始終返回"真"。
  • VBA基本語句
    條件表達式返回False時要執行的操作和計算 ELSEIF 條件表達式 THEN 條件表達式返回False時要執行的操作和計算 [ELSE 條件表達式返回False時要執行的操作和計算]END IF二、Select Case語句Select Case 表達式 Case Is 條件表達式
  • 小白篇(四):Mysql資料庫學習-Sql語句之DML語法
    一般包含:select、insert、update、delete 這些語法。在工作中如果能夠靈活並且熟練的使用這些語法。也可以算作一個Sql大神了。不要小看這些語法,工作中通常會有組合的使用情況。Sql是進入大數據的必備技能。DML操作實戰1、select語法:查詢表數據使用,可以同時關聯多種表進行查詢。但是需要弄清楚表中數據存儲的關係。
  • MySQL mysqlbinlog 解析出的 SQL 語句被注釋是怎麼回事
    OK, 0 rows affected (0.01 sec)  mysql> create table kkk (id int ,name varchar(32));Query OK, 0 rows affected (0.02 sec)  mysql> insert into kkk    -> select