追加関数: 同時アクセスに対してスレッドセーフではない
for ループ内のスライスに要素を追加するためにゴルーチンを同時に利用すると、異常が発生するデータ内で発生する可能性があります。結果のスライスに欠落したデータまたは空白のデータが表示される場合があり、データ競合の可能性を示しています。
これは、Go では同時読み取りと書き込みに対して本質的に安全な値がないために発生します。スライス ヘッダーによって表されるスライスも例外ではありません。提供されたコードは、同時アクセスによるデータ競合を示しています:
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 ...
同時実行の解決問題
この問題を解決するには、sync.Mutex:
var ( mu = &sync.Mutex{} 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 mu.Lock() destSlice = append(destSlice, tmpObj) mu.Unlock() }(myObject) } wg.Wait()
を使用して destSlice への書き込みアクセスを保護します。または、チャネルを使用して追加を非同期的に処理することを検討してください。
var ( appendChan = make(chan myClass) 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 appendChan <- tmpObj }(myObject) } go func() { for { tmpObj := <-appendChan destSlice = append(destSlice, tmpObj) } }() wg.Wait()
以上がGo の「append」関数が同時アクセスに対してスレッドセーフではないのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。