2013-04-15 40 views
4

與去打打左右,我扔在一起,這樣的代碼:寫這段代碼的更好的習慣用法?

package main 

import "fmt" 

const N = 10 

func main() { 
    ch := make(chan int, N) 
    done := make(chan bool) 

    for i := 0; i < N; i++ { 
     go (func(n int, ch chan int, done chan bool) { 
      for i := 0; i < N; i++ { 
       ch <- n*N + i 
      } 
      done <- true 
     })(i, ch, done) 
    } 

    numDone := 0 
    for numDone < N { 
     select { 
     case i := <-ch: 
      fmt.Println(i) 
     case <-done: 
      numDone++ 
     } 
    } 

    for { 
     select { 
     case i := <-ch: 
      fmt.Println(i) 
     default: 
      return 
     } 
    } 
} 

基本上我有N個頻道做了一些工作,並彙報其在同一通道 - 我想知道當所有的通道完成。所以我有這個其他的done頻道,每個工作者的goroutine發送消息(消息無關緊要),並且這導致main將該線程計數爲完成。當計數到達N時,我們實際上完成了。

這是「好」嗎?有沒有更習慣性的做法呢?

編輯:爲了澄清一點,我很懷疑,因爲done頻道似乎在做一個頻道關閉的工作,但我當然不能在任何goroutine中關閉頻道,因爲所有例程共享相同的頻道。所以我使用done來模擬某種「緩衝關閉」的頻道。

edit2:原來的代碼並沒有真正的工作,因爲有時來自例程的done信號在它剛剛放在ch之前的int被讀取。需要一個「清理」循環。

回答

10

這裏是一個習慣使用的sync.WaitGroup你學習

playground link

package main 

import (
    "fmt" 
    "sync" 
) 

const N = 10 

func main() { 
    ch := make(chan int, N) 
    var wg sync.WaitGroup 
    for i := 0; i < N; i++ { 
     wg.Add(1) 
     go func(n int) { 
      defer wg.Done() 
      for i := 0; i < N; i++ { 
       ch <- n*N + i 
      } 
     }(i) 
    } 
    go func() { 
     wg.Wait() 
     close(ch) 
    }() 
    for i := range ch { 
     fmt.Println(i) 
    } 
} 

注意兩個使用閉包走常規的定義,並注意第二go語句來等待所有例程完成,然後關閉通道,因此可以使用range

+1

哇!這是一些漂亮的代碼。 –

1

只需使用WaitGroup!它們是內置的基本功能,基本上可以讓你等待不同的goroutine中的內容完成。

http://golang.org/pkg/sync/#WaitGroup

至於你的疑惑,對事物的方式有關的是,被關閉通道(永久完成)並且與完成工作完成(暫時)是不同的。

+0

好吧,這讓我感覺在整體上感覺更好了「我覺得我應該使用'關閉'」的東西,但看到我的評論cthom06 - 我不知道如何使用'WaitGroup'這個。 – Alec

2

看起來像你想sync.WaitGrouphttp://golang.org/pkg/sync/#WaitGroup

+0

我會如何使用WaitGroup?也就是說,我在'wg.Add(N)'中執行了'wg.Add(N)'並在每個例行程序中調用'wg.Done()',但是在完成之前我怎麼才能從'ch'中讀取?我不能使用'for x:= range ch',因爲頻道仍然沒有關閉,對嗎? – Alec

+1

@alecbenzer關閉通道不會從中刪除值。所以'去func(){eg.Wait();關閉(CH); }()'應該這樣做。 – cthom06

1

在第一個近似的代碼似乎或多或少沒關係我。

關於細節,'ch'應該被緩衝。另外'done'頻道goroutine「會計」可能會被替換爲sync.WaitGroup。

0

如果你遍歷從夠程產生的價值,你可以直接在 通信信道迭代:

for value := range ch { 
    println(value) 
} 

爲此所需的唯一的事情是,該通道ch被關閉以後,或否則 循環將永遠等待新值。

當與sync.WaitGroup組合使用時,這將有效替代您的for numDone < N

0

我在處理與我的一些代碼相同的問題,發現this是一個比較適合的解決方案。

答案提供了Go的處理多個goroutines的習慣用法,它們都是通過單個通道發送的。