2016-08-16 3 views
3

次のコードは、時々2を出力します。待機グループがすべてのゴルーチンが完了するのを待っているのはなぜですか?なぜこのWaitGroupはすべてのゴルーチンを待たないのですか?

type Scratch struct { 
    //sync.RWMutex 
    Itch []int 
} 

func (s *Scratch) GoScratch(done chan bool, j int) error { 

    var ws sync.WaitGroup 

    if len(s.Itch) == 0 { 
      s.Rash = make([]int, 0) 
    } 
    for i := 0; i < j; i++ { 
      ws.Add(1) 
      go func (i int) { 
        defer ws.Done() 

        s.Rash = append(s.Rash, i) 
      }(i) 
    } 
    ws.Wait() 
    done<- true 
    return nil 
} 

func main() { 
    done := make(chan bool, 3) 
    s := &Scratch{} 
    err := s.GoScratch(done, 3) 
    if err != nil { 
      log.Println("Error:%v",err) 
    } 
    <-done 
    log.Println("Length: ", len(s.Rash)) 
}` 

奇妙なことに、私はmain関数で2を出力することはできませんが、テストケースを使用すると、時々2が出力されます。

答えて

5

コードに競合状態があります。それは右ここにある:すべてのゴルーチンが同時にs.Rashにアクセス

go func (i int) { 
    defer ws.Done() 
    // race condition on s.Rash access 
    s.Rash = append(s.Rash, i) 
}(i) 

ので、これはスライスの更新が上書きされる可能性があります。これを防ぐためにsync.Mutexロックと同じコードを実行してみましょう:

// create a global mutex 
var mutex = &sync.Mutex{} 

// use mutex to prevent race condition 
go func (i int) { 
    defer ws.Done() 
    defer mutex.Unlock() // ensure that mutex unlocks 

    // Lock the resource before accessing it 
    mutex.Lock() 
    s.Rash = append(s.Rash, i) 
}(i) 

あなたはこのherehereについての詳細を読むことができます。

+0

どのように私はそれを逃したのか分からない - 休憩を取る必要があります。ありがとう、 – Sridhar

+0

スライスでミューテックスを使用する代わりに、バッファリングされたチャネルを使用することもできます。 https://play.golang.org/p/CGdz4T2Qn5 – Billy

3

あなたはレース検出器

go test -race . 

でコードを実行する場合は、スライスs.Rash上の競合状態を見つけるでしょう。

関連する問題