どのようなInterlocked.CompareExchangeは、dapper .netメソッドで使用されていますか?

.net c# dapper thread-safety

質問

Link.TryAddメソッドのLink.TryAddコードには、次のコードがあります。

var snapshot = Interlocked.CompareExchange(ref head, null, null);

シンプルではなく、なぜこれが必要ですか?

var snapshot = head;

両方の行がheadの値を変更しない場合、両方の行はheadの値をsnapshot割り当てます。なぜ、最初のものが2番目に選ばれたのですか?

編集:私が参照しているコードはここにあります: https : //github.com/SamSaffron/dapper-dot-net/blob/77227781c562e65c167bf7a933d69291d5bdc6f3/Dapper/SqlMapper.cs

受け入れられた回答

彼らは揮発性の読み込みを行いたいが、汎用型のパラメータを取るThread.VolatileReadのオーバーロードはない。この方法でInterlocked.CompareExchangeを使用すると、同じ結果が得られます。

彼らが解決しようとしている問題は、JITコンパイラが、適切と見なされると、一時的な割り当てを最適化できるということです。現在のスレッドが一連の操作でそれを使用している間に、別のスレッドがヘッド参照を変更すると、スレッドの問題が発生する可能性があります。

編集:

問題は、 TryAdd開始時に古い値が読み取られるということではありません。問題は105行目で、現在のヘッドを前のヘッド( snapshot )と比較する必要があることです。最適化が存在する場合、以前の値を保持するsnapshot変数がなく、その時点でheadが再び読み取られます。 headが103行と105行の間で変更された可能性がありますが、 CompareExchange成功する可能性が非常に高いです。その結果、2つのスレッドがTryAdd同時に呼び出すと、リスト内のノードが失われます。


人気のある回答

マイクは正しいです:これはコードを破るJIT最適化を妨げています。

彼らは揮発性の構造体トリックを使用することができます: headを読んで、それを構造体のvolatileフィールドに代入します。次に、そのフィールドから読み取ると、揮発性の読み取りであることが保証されます。

構造体自体はまったく重要ではありません。重要なことは、変数をコピーするために揮発性のフィールドが使用されたことだけです。

そのように:

struct VolatileHelper<T> { public volatile T Value; }
...
var volatileHelper = new VolatileHelper<Field>();
volatileHelper.Value = head;
var snapshot = volatileHelper.Value;

うまくいけば、それはランタイムコストがありません。いずれにしても、コストはCPUメモリの一貫性トラフィックを引き起こしているインターロックされた操作よりも少なくなります。

実際、すべてのキャッシュアクセス(読み込み1つでも)がメモリ一貫性トラフィックを必要とするという事実は、これを低速のキャッシュにします!インターロックされた操作は、より多くのCPUで拡張されないシステムグローバルリソースです。インターロックされたアクセスでは、グローバルハードウェアロック(メモリアドレスごとに1つのアドレスしかありません)が使用されます。



ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow