3

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

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

単純ではなく、これが必要なのはなぜですか。

var snapshot = head;

両方の行はの値を変更しません。両方の行がの値をにhead割り当てます。なぜ最初のものが2番目よりも選ばれたのですか?headsnapshot

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

4

2 に答える 2

7

彼らは揮発性の読み取りをしたいのですThread.VolatileReadが、ジェネリック型パラメーターをとる過負荷はありません。Interlocked.CompareExchangeこの方法を使用すると、同じ結果が得られます。

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

編集:

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

于 2012-06-19T22:04:10.400 に答える
2

mike z は正しいです。これは、コードを壊す (合法的な) JIT 最適化を妨げています。

ただし、揮発性構造体のトリックを使用することもできました。それを読み取りhead、構造体の揮発性フィールドに割り当てます。次に、そのフィールドから読み取ると、揮発性読み取りであることが保証されます!

構造自体はまったく問題ありません。重要なのは、volatile フィールドを使用して変数をコピーしたことだけです。

そのように:

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

うまくいけば、ランタイム コストはかかりません。いずれにせよ、コストは、CPU メモリ コヒーレンシ トラフィックを引き起こしているインターロック操作よりも低くなります。

実際には、すべてのキャッシュ アクセス (読み取りアクセスであっても) がメモリ コヒーレンシ トラフィックを必要とするため、キャッシュが低速になります。インターロック操作は、CPU の増加に対応しないシステム グローバル リソースです。インターロック アクセスは、グローバル ハードウェア ロックを使用します (メモリ アドレスごとですが、ここでは 1 つのアドレスしかありません)。

于 2012-06-19T22:23:27.393 に答える