浅谈Go Channel 高级实践

channel æ¯ golang éç¸å½æ趣çä¸ä¸ªåè½ï¼å¨æä½¿ç¨ golang ç¼ç çç»éªéï¼å¤§é¨åäºä»¶é½ä¼æ¯å¨äº«å channel å goroutine éåçä¹è¶£ãæ以æ¬æ主è¦ä»ç» channel çä¸äºæ趣çç¨æ³ã

è¿éæ Oling Cat ç¿»è¯çGoç¼ç¨è¯­è¨è§èéå³äº channelï¼ä¿¡éï¼çæè¿°ï¼

ä¿¡éæä¾äºä¸ç§æºå¶ï¼å®å¨ä¸¤ä¸ªå¹¶åæ§è¡çå½æ°ä¹é´è¿è¡åæ­¥ï¼å¹¶éè¿ä¼ éï¼ä¸è¯¥ä¿¡éåç´ ç±»åç¸ç¬¦çï¼å¼æ¥è¿è¡éä¿¡ã

è¿ä¸ªä¸ªæè¿°åä¹å³ãåæ¯ç¥ãå¨æ第ä¸æ¬¡é读çæ¶åï¼å®å¨ä¸æç½è¿å°åºæ¯ä¸ªä»ä¹ç©æãäºå®ä¸ï¼å¯ä»¥è®¤ä¸º channel æ¯ä¸ä¸ªç®¡éæèåè¿ååºéåï¼é常ç®åä¸è½»éãchannel 并ä¸æ¯ Golang é¦åçãå®åæ ·ä½ä¸ºåç½®åè½åºç°å¨å¶ä»è¯­è¨ä¸­ãå¨å¤§å¤æ°æåµä¸ï¼å®æ¯ä¸ä¸ªå大ãå笨ãåå¤æçæ¶æ¯éåç³»ç»çä¸ä¸ªåè½ã

æ¬æ主è¦è®²å®è·µï¼åçé¨åä¼ä¸ç¬å¸¦è¿ï¼å³äº go 语è¨å¹¶åå®ç°ååå­æ¨¡ååç»­ä¼ææç« ã

channel å®ç°çæºç ä¸å¤æï¼æ¨èé读ï¼https://github.com/golang/go/blob/master/src/runtime/chan.go

channel æ¯å¹²ä»ä¹ç

æä¹ï¼channel æ¯ç¨æ¥éä¿¡ç

å®éä¸ï¼ï¼æ°æ®æ·è´äºä¸ä»½ï¼å¹¶éè¿ channel ä¼ éï¼æ¬è´¨å°±æ¯ä¸ªéåï¼

channel åºè¯¥ç¨å¨ä»ä¹å°æ¹

æ ¸å¿ï¼éè¦éä¿¡çå°æ¹

ä¾å¦ä»¥ä¸åºæ¯ï¼

  • éç¥å¹¿æ­
  • 交æ¢æ°æ®
  • æ¾å¼åæ­¥
  • 并åæ§å¶
  • ...

è®°ä½ï¼channel ä¸æ¯ç¨æ¥å®ç°éæºå¶çï¼è½ç¶æäºå°æ¹å¯ä»¥ç¨å®æ¥å®ç°ç±»ä¼¼è¯»åéï¼ä¿æ¤ä¸´çåºçåè½ï¼ä½ä¸è¦è¿ä¹ç¨ï¼

channel ç¨ä¾å®ç°

è¶æ¶æ§å¶

// å©ç¨ time.After å®ç°
func main() {
  done := do()
  select {
  case <-done:
    // logic
  case <-time.After(3 * time.Second):
    // timeout
  }
}

func do() <-chan struct{} {
  done := make(chan struct{})
  go func() {
    // do something
    // ...
    done <- struct{}{}
  }()
  return done
}

åæå¿«çç»æ

æ¯è¾å¸¸è§çä¸ä¸ªåºæ¯æ¯éè¯ï¼ç¬¬ä¸ä¸ªè¯·æ±å¨æå®è¶æ¶æ¶é´å没æè¿åç»æï¼è¿æ¶éè¯ç¬¬äºæ¬¡ï¼å两次中æå¿«è¿åçç»æ使ç¨ã
è¶æ¶æ§å¶å¨ä¸é¢æï¼ä¸é¢ä»£ç é¨åå°±ç®åå®ç°è°ç¨å¤æ¬¡äºã

func main() {
  ret := make(chan string, 3)
  for i := 0; i < cap(ret); i++ {
    go call(ret)
  }
    fmt.Println(<-ret)
}

func call(ret chan<- string) {
  // do something
  // ...
  ret <- "result"
}

éå¶æ大并åæ°

// æ大并åæ°ä¸º 2
limits := make(chan struct{}, 2)
for i := 0; i < 10; i++ {
  go func() {
    // ç¼å²åºæ»¡äºå°±ä¼é»å¡å¨è¿
    limits <- struct{}{}
    do()
    <-limits
  }()
}

for...range ä¼å

for ... range c { do } è¿ç§åæ³ç¸å½äº if _, ok := <-c; ok { do }

func main() {
  c := make(chan int, 20)
  go func() {
    for i := 0; i < 10; i++ {
      c <- i
    }
    close(c)
  }()
  // å½ c 被å³é­åï¼åå®éé¢çåç´ å°±ä¼è·³åºå¾ªç¯
  for x := range c {
    fmt.Println(x)
  }
}

å¤ä¸ª goroutine åæ­¥ååº

å©ç¨ close 广æ­

func main() {
  c := make(chan struct{})
  for i := 0; i < 5; i++ {
    go do(c)
  }
  close(c)
}

func do(c <-chan struct{}) {
  // ä¼é»å¡ç´å°æ¶å° close
  <-c
  fmt.Println("hello")
}

éé»å¡ç select

select æ¬èº«æ¯é»å¡çï¼å½ææåæ¯é½ä¸æ»¡è¶³å°±ä¼ä¸ç´é»å¡ï¼å¦ææ³ä¸é»å¡ï¼é£ä¹ä¸ä¸ªä»ä¹é½ä¸å¹²ç default åæ¯æ¯æ好çéæ©

select {
case <-done:
  return
default:  
}

for{select{}} ç»æ­¢

å°½éä¸è¦ç¨ break label å½¢å¼ï¼èæ¯æç»æ­¢å¾ªç¯çæ¡ä»¶æ¾å° for æ¡ä»¶éæ¥å®ç°

for ok {
  select {
  case ch <- 0:
  case <-done:
    ok = false
  }
}

channel ç¹æ§

åºç¡ç¹æ§

æä½ å¼ä¸º nil ç channel 被å³é­ç channel 正常ç channel
close panic panic æåå³é­
c<- æ°¸è¿é»å¡ panic é»å¡ææååé
<-c æ°¸è¿é»å¡ æ°¸è¿ä¸é»å¡ é»å¡ææåæ¥æ¶

happens-before ç¹æ§

  1. æ ç¼å²æ¶ï¼æ¥æ¶ happens-before åé
  2. ä»»ä½æåµä¸ï¼åé happens-before æ¥æ¶
  3. close happens-before æ¥æ¶

åè

https://go101.org/article/channel.html
https://golang.org/doc/effective_go.html#channels

以ä¸å°±æ¯æ¬æçå¨é¨å容ï¼å¸æ对大家ç学习ææ帮å©ï¼ä¹å¸æ大家å¤å¤æ¯æèæ¬ä¹å®¶ã