什麼Interlocked.CompareExchange用於dapper .net方法?

.net c# dapper thread-safety

Link.TryAdd方法的dapper代碼中,有以下代碼:

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

為什麼這需要而不是簡單:

var snapshot = head;

兩行都不會更改head的值,兩行都將head的值賦值給snapshot 。為什麼在第二個選擇第一個?

編輯:我所指的代碼在這裡: https//github.com/SamSaffron/dapper-dot-net/blob/77227781c562e65c167bf7a933d69291d5bdc6f3/Dapper/SqlMapper.cs

一般承認的答案

他們想要進行易失性讀取,但Thread.VolatileRead沒有帶有泛型類型參數的重載。使用Interlocked.CompareExchange這種方式可以獲得相同的結果。

他們試圖解決的問題是JIT編譯器如果認為合適,可以優化對temp的賦值。如果另一個線程在當前線程在一系列操作中使用它時突變頭引用,則會導致線程問題。

編輯:

問題不在於在TryAdd開頭讀取過時值。問題是,在第105行,他們需要將當前頭部與前一個頭部(保持在snapshot )進行比較。如果存在優化,則沒有保存先前值的snapshot變量,並且在該點再次讀取head 。即使行103和105之間的頭可能已經改變, CompareExchange很可能成功。結果是如果兩個線程同時調用TryAdd ,則列表中的節點將丟失。


熱門答案

mike z是對的:這阻止了(合法的)JIT優化會破壞代碼。

但是,他們本可以使用volatile struct技巧 :讀取head並將其分配給某個struct的volatile字段。接下來,從該字段讀取它,並保證是易失性讀取!

結構本身根本不重要。重要的是,使用volatile字段來複製變量。

像那樣:

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

希望它沒有運行時成本。在任何情況下,成本都低於導致CPU內存一致性流量的互鎖操作。

實際上,每個緩存訪問(甚至是讀取緩存)需要內存一致性流量這一事實使得緩存速度變慢 !互鎖操作是一種系統全局資源,不能隨著更多CPU擴展。互鎖訪問使用全局硬件鎖(每個內存地址,但這裡只有一個地址)。



許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因