2017-05-24 12 views
2

は私がforループ内ゴルーチンを使用してスライスに追加しようとした場合、私は/ブランクデータの欠落になるだろうインスタンスが存在することになることに気づい:スレッドセーフではありませんか?

時には
destSlice := make([]myClass, 0) 

var wg sync.WaitGroup 
for _, myObject := range sourceSlice { 
    wg.Add(1) 
    go func(closureMyObject myClass) { 
     defer wg.Done() 
     var tmpObj myClass 
     tmpObj.AttributeName = closureMyObject.AttributeName 
     destSlice = append(destSlice, tmpObj) 
    }(myObject) 
} 
wg.Wait() 

、私はdestSliceからすべてのAttributeName Sを印刷するとき、いくつかの要素は空の文字列("")ですが、sourceSliceのいくつかの要素はdestSliceに存在しません。

私のコードにはデータ競合がありますが、これはappendが複数のゴルーチンによって同時に使用するためのスレッドセーフではないことを意味しますか?

答えて

4

Goでは、読み書きを同時に行うことはできません。スライス(slice headers)も例外ではありません。

はい、あなたのコードは、データ競合を持っています。確認のために-raceオプションで実行してください。

例:

type myClass struct { 
    AttributeName string 
} 
sourceSlice := make([]myClass, 100) 

destSlice := make([]myClass, 0) 

var wg sync.WaitGroup 
for _, myObject := range sourceSlice { 
    wg.Add(1) 
    go func(closureMyObject myClass) { 
     defer wg.Done() 
     var tmpObj myClass 
     tmpObj.AttributeName = closureMyObject.AttributeName 
     destSlice = append(destSlice, tmpObj) 
    }(myObject) 
} 
wg.Wait() 

go run -race play.go 

出力とそれを実行すると、次のとおりです。

================== 
WARNING: DATA RACE 
Read at 0x00c420074000 by goroutine 6: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0x69 

Previous write at 0x00c420074000 by goroutine 5: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0x106 

Goroutine 6 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 

Goroutine 5 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 
================== 
================== 
WARNING: DATA RACE 
Read at 0x00c42007e000 by goroutine 6: 
    runtime.growslice() 
     /usr/local/go/src/runtime/slice.go:82 +0x0 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0x1a7 

Previous write at 0x00c42007e000 by goroutine 5: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0xc4 

Goroutine 6 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 

Goroutine 5 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 
================== 
================== 
WARNING: DATA RACE 
Write at 0x00c420098120 by goroutine 80: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0xc4 

Previous write at 0x00c420098120 by goroutine 70: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0xc4 

Goroutine 80 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 

Goroutine 70 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 
================== 
Found 3 data race(s) 
exit status 66 

ソリューションは単純で、destSlice値を書き込む保護するためにsync.Mutexを使用

destSlice := make([]myClass, 0) 
mux := &sync.Mutex{} 

var wg sync.WaitGroup 
for _, myObject := range sourceSlice { 
    wg.Add(1) 
    go func(closureMyObject myClass) { 
     defer wg.Done() 
     var tmpObj myClass 
     tmpObj.AttributeName = closureMyObject.AttributeName 
     mux.Lock() 
     destSlice = append(destSlice, tmpObj) 
     mux.Unlock() 
    }(myObject) 
} 
wg.Wait() 

あなたはまた、例えば、他の方法でそれを解決することができ追加する値を送るチャンネルを使用し、指定されたゴルーチンをこのチャンネルから受信して追加することができます。

関連する問題