日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

Go?select使用與底層原理講解_Golang

作者:樹獺叔叔??????? ? 更新時(shí)間: 2022-09-24 編程語言

1. select的使用

select 是 Go 提供的 IO 多路復(fù)用機(jī)制,可以用多個(gè) case 同時(shí)監(jiān)聽多個(gè) channl 的讀寫狀態(tài):

  • case: 可以監(jiān)聽 channl 的讀寫信號(hào)
  • default:聲明默認(rèn)操作,有該字段的 select 不會(huì)阻塞
select {
case chan <-:
    // TODO
case <- chan:
    // TODO
default:
    // TODO
}

2. 底層原理

  • 每一個(gè) case 對(duì)應(yīng)的 channl 都會(huì)被封裝到一個(gè)結(jié)構(gòu)體中;
  • 當(dāng)?shù)谝淮螆?zhí)行到 select 時(shí),會(huì)鎖住所有的 channl 并且,打亂 case 結(jié)構(gòu)體的順序;
  • 按照打亂的順序遍歷,如果有就緒的信號(hào),就直接走對(duì)應(yīng) case 的代碼段,之后跳出 select;
  • 如果沒有就緒的代碼段,但是有 default 字段,那就走 default 的代碼段,之后跳出 select;
  • 如果沒有 default,那就將當(dāng)前 goroutine 加入所有 channl 的對(duì)應(yīng)等待隊(duì)列;
  • 當(dāng)某一個(gè)等待隊(duì)列就緒時(shí),再次鎖住所有的 channl,遍歷一遍,將所有等待隊(duì)列中的 goroutine 取出,之后執(zhí)行就緒的代碼段,跳出select。

3. 數(shù)據(jù)結(jié)構(gòu)

每一個(gè) case 對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)如下:

type scase struct {
    c           *hchan         // chan
    elem        unsafe.Pointer // 讀或者寫的緩沖區(qū)地址
    kind        uint16   //case語句的類型,是default、傳值寫數(shù)據(jù)(channel <-) 還是  取值讀數(shù)據(jù)(<- channel)
    pc          uintptr // race pc (for race detector / msan)
    releasetime int64
}

4. 幾種常見 case

學(xué)習(xí)了 select 的使用與原理,我們就能更輕松地分辨不同情況下的輸出情況了。

case 1

package main

import (
  "fmt"
  "time"
)

func main() {
  chan1 := make(chan int)
  chan2 := make(chan int)
  go func() {
    chan1 <- 1
    time.Sleep(5 * time.Second)
  }()
  go func() {
    chan2 <- 1
    time.Sleep(5 * time.Second)
  }()
  select {
    case <- chan1:
      fmt.Println("chan1")
    case <- chan2:
      fmt.Println("chan2")
    default:
      fmt.Println("default")
  }
}

三種輸出都有可能。

case2

package main

import (
  "fmt"
  "time"
)
func main() {
  chan1 := make(chan int)
  chan2 := make(chan int)
  
  select {
    case <- chan1:
      fmt.Println("chan1")
    case <- chan2:
      fmt.Println("chan2")
  }
  fmt.Println("main exit.")
}

上述程序會(huì)一直阻塞。

case3

package main

import (
  "fmt"
)

func main() {
  chan1 := make(chan int)
  chan2 := make(chan int)
  
  go func() {
    close(chan1)
  }()
  go func() {
    close(chan2)
  }()
  select {
    case <- chan1:
      fmt.Println("chan1")
    case <- chan2:
      fmt.Println("chan2")
  }
  fmt.Println("main exit.")
}

隨機(jī)執(zhí)行1或者2.

case4

package main

func main() {
  select {
  }
}

對(duì)于空的 select 語句,程序會(huì)被阻塞,確切的說是當(dāng)前協(xié)程被阻塞,同時(shí) Go 自帶死鎖檢測(cè)機(jī)制,當(dāng)發(fā)現(xiàn)當(dāng)前協(xié)程再也沒有機(jī)會(huì)被喚醒時(shí),則會(huì)發(fā)生 panic。所以上述程序會(huì) panic。

原文鏈接:https://juejin.cn/post/7123037385419407374

欄目分類
最近更新