同時スライス: スレッドセーフではない追加
質問:
次のコードを考えてみましょう。 for 内で複数のゴルーチンを使用してスライスに追加するloop:
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 に欠落データまたは空のデータが生成されたり、sourceSlice のすべての要素が含まれなかったりします。なぜこのようなことが起こるのでしょうか?
答え:
Go では、スライスを含む同時読み取り/書き込み操作を受けると、すべての値がデータ競合の影響を受けやすくなります。これは、スライス ヘッダー (スライスの容量と長さに関する情報が含まれる) の実装方法によるものです。
-race フラグを指定してコードを実行すると、データ競合の存在が確認されます。
go run -race play.go
解決策:
データ競合を回避し、ファイルに追加する際の同時安全性を確保するにはスライスの場合、sync.Mutex などの同期プリミティブを使用して、destSlice 値の書き込みを保護する必要があります。
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()
あるいは、同時追加プロセスを促進するためにチャネルの使用を検討してください。
以上が同時実行ゴルーチンでのスライスへの追加がスレッドセーフではないのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。