同時実行ゴルーチンでのスライスへの追加がスレッドセーフではないのはなぜですか?

Linda Hamilton
リリース: 2024-11-10 00:49:02
オリジナル
759 人が閲覧しました

Why is Appending to a Slice in Concurrent Goroutines Not Thread-Safe?

同時スライス: スレッドセーフではない追加

質問:

次のコードを考えてみましょう。 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 サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート