2016-03-17 6 views
9

私は、同時ゴーライブラリに取り組んでいる、と私は結果と類似しているゴルーチン間の同期の二つの異なるパターンつまずい:sync.WaitGroup over Advantageの利点は何ですか?

チャンネルを使用してWaitgroup

var wg sync.WaitGroup 
func main() { 
     words := []string{ "foo", "bar", "baz" } 

     for _, word := range words { 
       wg.Add(1) 
       go func(word string) { 
         time.Sleep(1 * time.Second) 
         defer wg.Done() 
         fmt.Println(word) 
       }(word) 
     } 
     // do concurrent things here 

     // blocks/waits for waitgroup 
     wg.Wait() 
} 

を使用して

func main() { words = []string{ "foo", "bar", "baz" } done := make(chan bool) defer close(done) for _, word := range words { go func(word string) { time.Sleep(1 * time.Second) fmt.Println(word) done <- true }(word) } // Do concurrent things here // This blocks and waits for signal from channel <-done } 

私はsync.WaitGroupがわずかによりパフォーマンスで、I hのことを助言されましたそれが一般的に使用されているのを見た。しかし、私はより慣用的なチャンネルを見つける。チャネル上でsync.WaitGroupを使用することの本当のメリットは何ですか?

+1

2番目の例では、同期が間違っています。最初のゴルーチンが最後のチャンネルまでチャンネルを送信するまでブロックします。私はそれがすべてのメンバーをプリントアウトword' 'にゴルーチンの機能に引数を変更したときに何らかの理由でhttps://github.com/golang/go/wiki/MutexOrChannel#wait-group –

+2

はに見てください正しく – molivier

+0

@Not_a_Golfer: – PieOhPah

答えて

17

あなたの第二の例の正しさのIndependantly(コメントで説明したように、あなたは何を考えてやっていませんが、それは簡単に修正可能です)、私は最初の例は把握しやすいことを考える傾向があります。

今、私は偶数チャネルは、より慣用的であることを言わないだろう。 Go言語の署名機能であるチャネルは、可能な限りいつでも使用することが慣用的であるとは限りません。ここで、WaitGroupは意味(あなたの主な機能は行うべき労働者のためのWait INGである)とメカニックの両方を伝える(彼らはDoneあるとき労働者が通知):何ゴーで慣用であることは解決策を理解するのが最も簡単かつ最も簡単に使用することです。

あなたは非常に具体的なケースにしていない限り、私はここで、チャネル・ソリューションを使用してrecommandない...

2

だけのチャンネルを使用する方法について、特に粘着性のある場合は、我々が使用している場合、それは(別々に行う必要がありますあなたの例は@Not_a_Golferが指摘するように、間違った結果を出すでしょう)。

一つの方法は、int型のチャンネルを作ることです。ワーカープロセスでは、ジョブが完了するたびに番号を送信します(受信者でこれを追跡できるようにするには、これも一意のジョブIDにすることができます)。

(受信したジョブの正確な数を知る)レシーバメインルーチンでは、チャネル上で範囲ループを実行し、送信されたジョブの数が完了しなくなるまでカウントし、ジョブが完了しました。これは、各ジョブの完了を追跡したい場合(そして、必要に応じて何かを実行する場合)、良い方法です。

ここに参考になるコードがあります。 totalJobsLeftを減らすことは、チャンネルの範囲ループ内でのみ実行されるので安全です!

//This is just an illustration of how to sync completion of multiple jobs using a channel 
//A better way many a times might be to use wait groups 

package main 

import (
    "fmt" 
    "math/rand" 
    "time" 
) 

func main() { 

    comChannel := make(chan int) 
    words := []string{"foo", "bar", "baz"} 

    totalJobsLeft := len(words) 

    //We know how many jobs are being sent 

    for j, word := range words { 
     jobId := j + 1 
     go func(word string, jobId int) { 

      fmt.Println("Job ID:", jobId, "Word:", word) 
      //Do some work here, maybe call functions that you need 
      //For emulating this - Sleep for a random time upto 5 seconds 
      randInt := rand.Intn(5) 
      //fmt.Println("Got random number", randInt) 
      time.Sleep(time.Duration(randInt) * time.Second) 
      comChannel <- jobId 
     }(word, jobId) 
    } 

    for j := range comChannel { 
     fmt.Println("Got job ID", j) 
     totalJobsLeft-- 
     fmt.Println("Total jobs left", totalJobsLeft) 
     if totalJobsLeft == 0 { 
      break 
     } 
    } 
    fmt.Println("Closing communication channel. All jobs completed!") 
    close(comChannel) 

} 
関連する問題