天天看點

了解golang中關鍵字-chan&select

chan

channel直譯過來就是管道,chan關鍵字定義了goroutine中的管道通信,一個goroutine可以和另一個goroutine進行通信。

chan的讀寫和定義如下:

//define a chan type variable
var ch chan int = make(chan int, 10);
//or
ch := make(chan int, 10);

//write data into chan 
ch <- 1;
ch <- 2;

//read data from chan 
var x int;
x = <-ch;
x = <-ch;      

看下面這段代碼,其中x擷取ch隊頭值,nonempty傳回ch是否非空的:

func main() {
  var ch chan int = make(chan int, 10)
  ch <- 1
  ch <- 2
  ch <- 3
  close(ch)

  var x int
  var nonempty bool
  x, nonempty = <-ch
  fmt.Println("x =", x, " length of ch is", len(ch), "nonempty", nonempty)
  x, nonempty = <-ch
  fmt.Println("x =", x, " length of ch is", len(ch), "nonempty", nonempty)
  x, nonempty = <-ch
  fmt.Println("x =", x, " length of ch is", len(ch), "nonempty", nonempty)
  x, nonempty = <-ch
  fmt.Println("x =", x, " length of ch is", len(ch), "nonempty", nonempty)
}      

列印結果:

了解golang中關鍵字-chan&amp;select

可以看到chan是類似隊列的先進先出類型,每次讀出一個資料後,ch自動彈出該資料不再儲存。

chan一定要初始化才能進行讀寫操作,否則産生死鎖:

package main

import (
  "fmt"
)

func main() {
  var ch chan int// = make(chan int, 10)
  ch <- 1
  ch <- 2
  ch <- 3
  close(ch)

  var x int
  var nonempty bool
  x, nonempty = <-ch
  fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
  x, nonempty = <-ch
  fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
  x, nonempty = <-ch
  fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
      x, nonempty = <-ch
  fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
}      

執行結果,由于所有goroutine已經睡眠,産生死鎖: 

了解golang中關鍵字-chan&amp;select

select

select會執行第一個可以執行的case語句,會一直監聽直到有第一個可以執行的語句。或者換一種說法,select就是用來監聽和channel有關的IO操作,當 IO 操作發生時,觸發相應的動作。

看這段代碼:

package main

import (
  "fmt"
  "time"
)

func main() {
  var ch chan int = make(chan int, 10)
  var x int = 0
  var nonempty bool

  go func() {
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
    x, nonempty = <-ch
    fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
    x, nonempty = <-ch
    fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
    x, nonempty = <-ch
    fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
    x, nonempty = <-ch
    fmt.Println("x =", x, ", length of ch is", len(ch), ", nonempty", nonempty)
  }()
  select {
  case ch <- x:
    fmt.Println("ch write", x)
  case x = <-ch:
    fmt.Println("ch read", x)
  default:
    fmt.Println("nothing to do")
  }
  time.Sleep(time.Second)
}      

執行結果,寫入0:

了解golang中關鍵字-chan&amp;select

注釋掉第一個case:

select {
  // case ch <- x:
  //  fmt.Println("ch write", x)
  case x = <-ch:
    fmt.Println("ch read", x)
  default:
    fmt.Println("nothing to do")
  }      

 執行結果,由于ch為空無法讀出,運作default:

了解golang中關鍵字-chan&amp;select

如果有多個io操作都可以執行的話,select會随機選擇一個執行:

select {
  // case ch <- x:
  //  fmt.Println("ch write", x)
  case ch <- 4:
    fmt.Println("ch write 4")
  case ch <- 5:
    fmt.Println("ch write 5")
  case x = <-ch:
    fmt.Println("ch read", x)
  default:
    fmt.Println("nothing to do")
  }      

兩種結果: