Go之go與channel組合使用

1,等待一個事件
<- ch 將一直阻塞,直到ch被關閉 或者 ch中能夠取出值 爲止
因此到第17行以後會去執行go後面的func()匿名函數,在裏面給ch賦值後(或者close(ch))後,才能繼續日後執行
  1. package main
  2. import(
  3. "fmt"
  4. )
  5. func main(){
  6. fmt.Println("Begin doing something!")
  7. ch := make(chan int)
  8. go func(){
  9. fmt.Println("Doing something…")
  10. ch <-22
  11. //close(ch)
  12. }()
  13. <-ch //此處將被阻塞,直到ch被關閉 或 有值能夠取出
  14. fmt.Println("Done!")
  15. }
 
2,協同多個Goroutines
同上,close channel還能夠用於協同多個Goroutines,好比下面這個例子,咱們建立了100個Worker Goroutine,這些Goroutine在被建立出來後都阻塞在"<-start"上,直到咱們在main goroutine中給出開工的信號:"close(start)",這些goroutines纔開始真正的併發運行起來。
  1. package main
  2. import"fmt"
  3. func worker(start chan bool, index int){
  4. <-start // 從start中取出數據後,調用20行的case語句
  5. fmt.Println("This is Worker:", index)
  6. }
  7. func main(){
  8. start := make(chan bool)
  9. for i :=1; i <=10; i++{
  10. go worker(start, i)
  11. }
  12. //給start賦值10次,讓worker方法執行10次
  13. for i :=1; i <=10; i++{
  14. start <-true//給start賦值一次,便執行worker函數一次
  15. }
  16. v :=1
  17. //select 被一直阻塞直到start中數據被取出
  18. select{  //deadlock we expected
  19. case<-start:
  20. fmt.Print(v)
  21. v++
  22. }
  23. }
3,Select
  • select常與for一塊兒使用
  1. for{
  2. select{
  3. case x :=<- somechan:
  4. // … 使用x進行一些操做
  5. case y, ok :=<- someOtherchan:
  6. // … 使用y進行一些操做,
  7. // 檢查ok值判斷someOtherchan是否已經關閉
  8. case outputChan <- z:
  9. // … z值被成功發送到Channel上時
  10. default:
  11. // … 上面case均沒法通訊時,執行此分支
  12. }
  13. }
  • 終結woker
 
下面是一個常見的終結sub worker goroutines的方法,
每一個worker goroutine經過select監視一個die channel來及時獲取main goroutine的退出通知。
  1. package main
  2. import(
  3. "fmt"
  4. "time"
  5. )
  6. func worker(die chan bool, index int){
  7. fmt.Println("Begin: This is Worker:", index)
  8. for{
  9. select{
  10. //case xx:
  11. //作事的分支
  12. case<-die: //到這裏就被阻塞,運行main中的close後輸出 done
  13. fmt.Println("Done: This is Worker:", index)
  14. return
  15. }
  16. }
  17. }
  18. func main(){
  19. die:= make(chan bool)
  20. for i :=1; i <=10; i++{
  21. go worker(die, i)
  22. }
  23. time.Sleep(time.Second*5)
  24. close(die)
  25. select{}//deadlock we expected
  26. }
 
  • 終結驗證
有時候終結一個worker後,main goroutine想確認worker routine是否真正退出了,可採用下面這種方法:
  1. package main
  2. import(
  3. "fmt"
  4. //"time"
  5. )
  6. func worker(die chan bool){
  7. fmt.Println("Begin: This is Worker")
  8. for{
  9. select{
  10. //case xx:
  11. //作事的分支
  12. case<-die://這裏等待27行的賦值語句,若是沒有賦值,一直阻塞
  13. fmt.Println("Done: This is Worker")
  14. die<-true
  15. return
  16. }
  17. }
  18. }
  19. func main(){
  20. die:= make(chan bool)
  21. go worker(die)
  22. die<-true
  23. istrue :=<-die//這裏等待16行的賦值,賦值完畢後程序繼續執行
  24. fmt.Println("Worker goroutine has been terminated", istrue)
  25. }
  • 關閉的Channel永遠不會阻塞
  1. package main
  2. import"fmt"
  3. func main(){
  4. cb := make(chan bool)
  5. close(cb)//當cb被關閉後,全部的取值操做將不會被阻塞
  6. x :=<-cb
  7. fmt.Printf("%#v\n", x)
  8. x, ok :=<-cb
  9. fmt.Printf("%#v %#v\n", x, ok)
  10. ci := make(chan int)
  11. close(ci)
  12. y :=<-ci //即便ci被關閉,照樣能夠從ci中取數據,取得0
  13. fmt.Printf("%#v\n", y)
  14. cb <-true
  15. }

false緩存

false false併發

0函數

panic: send on closed channel spa

19行將報異常code

能夠看到在一個已經close的unbuffered channel上執行讀操做,回返回channel對應類型的零值,好比bool型channel返回false,int型channel返回0。但向close的channel寫則會觸發panic。不過不管讀寫都不會致使阻塞。隊列

 

  • (5)關閉帶緩存的channel
 
  1. package main
  2. import"fmt"
  3. func main(){
  4. c := make(chan int,3)
  5. c <-15
  6. c <-34
  7. c <-65
  8. close(c)
  9. fmt.Printf("%d\n",<-c)//channel被關閉後,照樣能夠從channel中取出數據,只是不能向其中寫數據
  10. fmt.Printf("%d\n",<-c)
  11. fmt.Printf("%d\n",<-c)
  12. fmt.Printf("%d\n",<-c)//當channel中數據所有被取出時,將輸出0
  13. c <-1
  14. }
15
34
65
0
panic: runtime error: send on closed channel 
16行將報異常
 
能夠看出帶緩衝的channel略有不一樣。儘管已經close了,但咱們依舊能夠從中讀出關閉前寫入的3個值。第四次讀取時,則會返回該channel類型的零值。向這類channel寫入操做也會觸發panic。
 
  • range
Golang中的range經常和channel並肩做戰,它被用來從channel中讀取全部值。下面是一個簡單的實例:
  1. package main
  2. import"fmt"
  3. func generator(strings chan string){
  4. strings <-"Five hour's New York jet lag"
  5. strings <-"and Cayce Pollard wakes in Camden Town"
  6. strings <-"to the dire and ever-decreasing circles"
  7. strings <-"of disrupted circadian rhythm."
  8. close(strings)
  9. }
  10. func main(){
  11. strings := make(chan string)
  12. go generator(strings)
  13. for s := range strings {
  14. fmt.Printf("%s\n", s)//這裏的s 至關於 <- strings,只有賦值以後才能讀取到,不然一直阻塞
  15. }
  16. fmt.Printf("\n")
  17. }
4. 隱藏狀態
沒有緩存 的chan默認爲1個單位的緩存
  1. package main
  2. import"fmt"
  3. func newUniqueIDService()<-chan string{
  4. id := make(chan string)
  5. go func(){
  6. var counter int64 =0
  7. for{
  8. id <- fmt.Sprintf("%x", counter)
  9. counter +=1
  10. }
  11. }()
  12. return id
  13. }
  14. func main(){
  15. id := newUniqueIDService()
  16. for i :=0; i <10; i++{
  17. fmt.Println(<-id) //被阻塞,直到id被賦值
  18. }
  19. }
 
newUniqueIDService經過一個channel與main goroutine關聯,main goroutine無需知道uniqueid實現的細節以及當前狀態,只需經過channel得到最新id便可。
 
 
5,select的default分支的實踐用法
  •        讀取時爲空
  1. idle := make(chan []byte,5)//用一個帶緩衝的channel構造一個簡單的隊列
  2. select{
  3. case<-idle:
  4. //嘗試從idle隊列中讀取
  5. fmt.Println("讀取")
  6. default://隊列空,分配一個新的buffer
  7. fmt.Println("寫入")
  8. }
  •     寫入時已滿
  1. package main
  2. import(
  3. "fmt"
  4. )
  5. func main(){
  6. idle := make(chan []int,10)//用一個帶緩衝的channel構造一個簡單的隊列
  7. var b =[]int{2,1}
  8. select{
  9. case idle <- b://嘗試向隊列中插入一個buffer
  10. fmt.Println(idle)
  11. default://隊列滿?
  12. println("隊列滿")
  13. }
  14. }
6,Nil Channels
 
  • nil channels阻塞 : 對一個沒有初始化的channel進行讀寫操做都將發生阻塞
 
  1. package main
  2. func main(){
  3. var c chan int
  4. c <-1
  5. }
將發生阻塞
 
  • 循環輸出
  1. package main
  2. import"fmt"
  3. import"time"
  4. func main(){
  5. var c1, c2 chan int= make(chan int), make(chan int)
  6. go func(){
  7. time.Sleep(time.Second*5)
  8. c1 <-5
  9. close(c1)
  10. }()
  11. go func(){
  12. time.Sleep(time.Second*7)
  13. c2 <-7
  14. close(c2)
  15. }()
  16. for{
  17. select{
  18. case x :=<-c1://在等待5s後,把值取出,若是close(c1)那麼,將一直輸出0
  19. fmt.Println(x)
  20. case x :=<-c2://在等待7s後,輸出c2的值並退出
  21. fmt.Println(x)
  22. return
  23. }
  24. }
  25. fmt.Println("over")
  26. }
輸出的結果是
5
0
0
0
...
7
 
改成交替輸出
  1. package main
  2. import"fmt"
  3. import"time"
  4. func main(){
  5. var c1, c2 chan int= make(chan int), make(chan int)
  6. go func(){
  7. time.Sleep(time.Second*5)
  8. c1 <-5
  9. close(c1)
  10. }()
  11. go func(){
  12. time.Sleep(time.Second*7)
  13. c2 <-7
  14. close(c2)
  15. }()
  16. for{
  17. select{
  18. case x, ok :=<-c1://若是c1未被關閉,則輸出x,若是x關閉,c1=nil
  19. if!ok {
  20. c1 =nil
  21. }else{
  22. fmt.Println(x)
  23. }
  24. case x, ok :=<-c2://若是c2未被關閉,則輸出x,若是x關閉,c2=nil
  25. if!ok {
  26. c2 =nil
  27. }else{
  28. fmt.Println(x)
  29. }
  30. }
  31. if c1 ==nil&& c2 ==nil{
  32. break//若是=nil那麼推出
  33. }
  34. }
  35. fmt.Println("over")
  36. }
5
7
over
 
 
7.Timers
  • 超時機制Timeout
帶超時機制的select是常規的tip,下面是示例代碼,實現30s的超時select:
 
  1. func worker(start chan bool){
  2. timeout := time.After(30* time.Second)
  3. for{
  4. select{
  5. // … do some stuff
  6. case<- timeout:
  7. return
  8. }
  9. }
  10. }
 
  • 心跳HeartBeart
與timeout實現相似,下面是一個簡單的心跳select實現:
  1. func worker(start chan bool){
  2. heartbeat := time.Tick(30* time.Second)
  3. for{
  4. select{
  5. // … do some stuff
  6. case<- heartbeat:
  7. //… do heartbeat stuff
  8. }
  9. }
  10. }
 



相關文章
相關標籤/搜索