不変クラスは、本質的にスレッドセーフです。
ほとんどの場合、同じデータの他の読み取りまたは書き込みと並行して書き込みを実行できるときに並行性の問題が発生します。
共有ロックと排他ロックを考慮してください。読み取りは共有ロックを取得して実行され、書き込みには排他ロックを取得する必要があります。任意の数のスレッドが同時に共有ロックを所有することができます。 1つのスレッドのみが排他ロックを同時に所有でき、排他ロックが保持されている間は共有ロックを保持できません。これは、書き込みを同時に実行することはできますが、書き込みも読み取りも書き込みも実行できないことを意味します。データを変更することができない場合は、並行性の問題は発生しません(排他ロックの必要がないため、共有ロックは意味をなさない)。
これは関数型言語の利点の1つです。データは決して変更されないため、本質的にスレッドセーフであり、積極的なコンパイラの最適化が可能です。
通常、スレッドセーフティについてはもう忘れられている質問があります。メモリモデル、特に現代のNUMAアーキテクチャです。
volatile変数について知っていれば、コンパイラは、シングルスレッド処理でprogranが正しいままである限り、データアクセスを自由に最適化できます。
他のスレッドが同時に変数を読み書きする可能性があることをコンパイラが認識していない場合、レジスタに値を保持して、メインメモリの変更をチェックしないことがあります。これは、異なるレベルのキャッシュにキャッシュされた値でも起こります。コンパイル時に条件の結果を知っていて、関係する値が非決定的に変化する可能性があることを知らない場合は、条件分岐を最適化することさえできます。
変数をvolatileと宣言すると、その値が変更され、毎回メインメモリにフラッシュされ、メインメニューからも読み込まれることを示します。
しかし、値が変更されない場合は、これがなぜ必要なのでしょうか?まあ、値は建設中に変化しますが、それは瞬間的または原子的であると仮定することはできません。コンパイラがマルチスレッドであることを知らない場合、メイン・メモリにデータをフラッシュすることさえありません。このオブジェクトへの参照を別のスレッドが利用できるようにすると、そのオブジェクトは初期化されていないメインメモリから読み込まれます。あるいは、初期化が行われているのを見ることもできます(これは、古いバージョンのjavaで大きなStringを初期化するときに起こる可能性があります)。
現代のC++標準ではメモリモデルが定義されていると思いますが、私はそれをまだ掘り下げていません。メモリモデルが指定されていない場合や十分に強くない場合は、ロックを取得または解放するなどのプリミティブを実行する必要があります。いずれにせよ、データが揮発性または不変であることをコンパイラに伝える必要があるため、使用中のメモリモデルの保証を提供することができます。
この場合、変数とgetterメソッドをconst修飾子で宣言します。私はそれが正常に動作することは確信していますが、私はあなたが使用している標準のメモリモデルを勉強し、必要に応じてより現代的な標準に切り替えることをお勧めします。
'SimpleClass'を構築していない限り、クラスの他のメソッドは副作用として' v'を変更することはできません。 – CoryKramer
データレースには*変更*アクセスが必要です。そのようなアクセス権がない場合は、レースの可能性はありません。 –
@KerrekSBあなたの説明は私の混乱を解決します。どうも! –