go select..case詳解


1. go select是一種僅能用於channel發送和接收消息的語句,此語句運行期間是阻塞的;當 select中沒有case語句的時候,會阻塞當前goroutine

2. select是go在語言層面提供的IO多路復用機制,專門用於檢測多個channel是否准備完畢:可讀可寫

3. select語句中除default外,每個case操作一個channel,要么讀要么寫

4. select語句中除default外,各case的執行順序是完全隨機的

5. select中如果沒有default語句,則會阻塞等待任一case

6. select語句中讀操作要判斷是否成功讀取,關閉的channel也可以讀取

 

實現原理:

Go在實現select時,定義了一個數據結構表示每一個case(包含default, default時一種特殊的case),select執行過程可以類比為一個函數,函數輸入case數組,輸出選中的case,然后程序流程轉到選中的case塊。

 

源碼包src/runtime/select.go:scase定義了表示case語句的數據結構:

 

type scase struct {
	c           *hchan         // chan,為當前case語句操作的channel指針,一個case語句只能操作一個channel
	elem        unsafe.Pointer // data element, 表示緩沖區地址,從channel中讀除的數據或者將要寫入channel的數據的存放地址 
	kind        uint16     // 表示case的類型,非為read write default 
	pc          uintptr // race pc (for race detector / msan)
	releasetime int64
}

  

源碼包src/runtime/select.go:selectgo()定義了select選擇case的函數,偽代碼如下:

func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
    //1. 鎖定scase語句中所有的channel
    //2. 按照隨機順序檢測scase中的channel是否ready
    //   2.1 如果case可讀,則讀取channel中數據,解鎖所有的channel,然后返回(case index, true)
    //   2.2 如果case可寫,則將數據寫入channel,解鎖所有的channel,然后返回(case index, false)
    //   2.3 所有case都未ready,則解鎖所有的channel,然后返回(default index, false)
    //3. 所有case都未ready,且沒有default語句
    //   3.1 將當前協程加入到所有channel的等待隊列
    //   3.2 當將協程轉入阻塞,等待被喚醒
    //4. 喚醒后返回channel對應的case index
    //   4.1 如果是讀操作,解鎖所有的channel,然后返回(case index, true)
    //   4.2 如果是寫操作,解鎖所有的channel,然后返回(case index, false)
}

  

參考文檔: https://my.oschina.net/renhc/blog/2253937

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM