作者 主題: [Golang] channel & for & select  (閱讀 3845 次)

0 會員 與 1 訪客 正在閱讀本文。

Yamaka

  • 俺是博士!
  • *****
  • 文章數: 4913
    • 檢視個人資料
    • http://www.ecmagic.com
[Golang] channel & for & select
« 於: 2015-08-06 18:15 »
今天看了這篇

Golang channels tutorial

算是這兩週所看過相關文章中,對 channel 使用有比較簡單清楚的說明

不過很可惜最後還是少了一種情況,就是當 channel 需要發送的資料數不確定時
文章最後範例是這樣:

代碼: [選擇]
package main

import (
"fmt"
"time"
)

func getMesgChannel(msg string, delay time.Duration) <-chan string {
c := make(chan string)
go func() {
for i := 1; i <= 3; i++ {
c <- fmt.Sprintf("%s, %d", msg, i)
time.Sleep(time.Millisecond * delay)
}
}()
return c
}

func main() {
c1 := getMesgChannel("001", 300)
c2 := getMesgChannel("002", 150)
c3 := getMesgChannel("003", 10)

for i := 1; i <= 9; i++ {
select {
case msg := <-c1:
fmt.Println(msg)
case msg := <-c2:
fmt.Println(msg)
case msg := <-c3:
fmt.Println(msg)
}
}
}

實際上在很多場合,如果 sender 不確定會發送多少資料出來
在接收端通常會用無窮迴圈來接收資料,例如:

代碼: [選擇]
  for {
select {
.....
}
}

但是這真的會造成無窮迴圈啊,發送端如果都停止送資料

fatal error: all goroutines are asleep - deadlock!

在 getMesgChannel() for{} 結束時關閉 channel 呢?
很可惜,一樣會造成 deadlock

我自己想到的方式是在 select 加一條 time.After的 case
超過多久時間沒收到任何訊息便結束迴圈

代碼: [選擇]
  for {
select {
case msg := <-c1:
fmt.Println(msg)
case msg := <-c2:
fmt.Println(msg)
case msg := <-c3:
fmt.Println(msg)
case <-time.After(time.Millisecond * 500):
fmt.Println("Done!")
break
}
}

不過這裡的 break 只會對 select 區塊有作用
還是無法跳出 for 迴圈,結果就是一直印出『Done!』

有兩個方式可以跳出 for 迴圈,一個是設變數來判斷
另一個是將 break 改成 return

代碼: [選擇]
  done := false
for {
select {
    ..... 
case <-time.After(time.Millisecond * 500):
fmt.Println("Done!")
return
done = true
}
if done {
break
}
}

or
代碼: [選擇]
    case <-time.After(time.Millisecond * 500):
fmt.Println("Done!")
return
完整範例:

或點這裡

代碼: [選擇]
package main

import (
"fmt"
"time"
)

func getMesgChannel(msg string, count int, delay time.Duration) <-chan string {
c := make(chan string)
go func() {
for i := 1; i <= count; i++ {
c <- fmt.Sprintf("%s, %d", msg, i)
time.Sleep(time.Millisecond * delay)
}
}()
return c
}

func main() {
c1 := getMesgChannel("001", 5, 300)
c2 := getMesgChannel("002", 8, 200)
c3 := getMesgChannel("003", 13, 10)

done := false
for {
select {
case msg := <-c1:
fmt.Println(msg)
case msg := <-c2:
fmt.Println(msg)
case msg := <-c3:
fmt.Println(msg)
case <-time.After(time.Millisecond * 500):
fmt.Println("Done!")
      done = true
//return
}
if done {
break
}
}
}

執行結果:

引用
$ time go run ex038.go
002, 1
003, 1
001, 1
003, 2
003, 3
003, 4
003, 5
003, 6
003, 7
003, 8
003, 9
003, 10
003, 11
003, 12
003, 13
002, 2
001, 2
002, 3
002, 4
001, 3
002, 5
001, 4
002, 6
002, 7
001, 5
002, 8
Done!

real   0m2.411s
user   0m0.552s
sys   0m0.284s