sync.WaitGroup 相当をチャネルのみで記述可能？


一般には [`sync.WaitGroup`](http://golang.org/pkg/sync/#WaitGroup)
で実現される並行処理の完了待機を、チャネル( [`chan`](http://www.golang-
book.com/10/index.htm#section2) )だけをつかって記述することはできますか？

[code]

    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
      wg.Add(1)
      go func(i int) {
        // 並行処理[i]
        wg.Done()
      }(i)
    }
    wg.Wait()
    
[/code]



[code]

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        done := make(chan bool)
        for i := 0; i < 10; i++ {
            go func(i int) {
                time.Sleep(time.Second * time.Duration(i))
                fmt.Println("Finish:", i)
                done <- true
            }(i)
        }
    
        for i := 0; i < 10; i++ {
            <-done
        }
        close(done)
        fmt.Println("All goroutines are done.")
    }
    
[/code]

なお、別解として以下の様にした場合、

[code]

    func main() {
        done := make(chan int)
        for i := 0; i < 10; i++ {
            go func(i int) {
                time.Sleep(time.Second * time.Duration(i))
                done <- i
            }(i)
        }
    
        for i := range done {
            fmt.Println(i)
        }
    
[/code]

~~`for loop` が終了した時点で以下の様なエラーが発生してしまいました。~~  
以下のエラーメッセージを出力して異常終了します。

[code]

    fatal error: all goroutines are asleep - deadlock!
    
[/code]

これは、`for ... range done` で 10 個の goroutine から値を取り出した後、さらに channel
からデータを読み込もうとしたためです。




ちょっと違うアプローチで。。  
goroutineの分だけchannel作ると言う手も。

[code]

    chs := make([]chan struct{}, 10)
    for i := 0; i < 10; i++ {
        chs[i] = make(chan struct{})
        go func(i int, ch chan struct{}) {
            // 並行処理[i]
            ch <- struct{}{}
        }(i, chs[i])
    }
    
    // wait
    for _, ch := range chs {
        <-ch
    }
    
[/code]

ちょっとコスト高かもですが、goroutineからerrorや戻り値受け取れるというメリットも。

