在大多數計算機上,增加變量操作不是一個原子操作,需要執行下列步驟:
一:將實例變量中的值加載到寄存器中。
二:增加或減少該值。
三:在實例變量中存儲該值。
在多線程環境下,線程會在執行完前兩個步驟后被搶先。然后由另一個線程執行所有三個步驟,當第一個線程重新開始執行時,它覆蓋實例變量中的值,造成第二個線程執行增減操作的結果丟失。
Interlocked可以為多個線程共享的變量提供原子操作。
Interlocked.Increment:以原子操作的形式遞增指定變量的值并存儲結果。
Interlocked.Decrement以原子操作的形式遞減指定變量的值并存儲結果。
Interlocked.Add以原子操作的形式,添加兩個整數并用兩者的和替換第一個整數
但是Interlocked并沒有為乘法,除法提供原子操作。那么如何實現乘法,除法,以及為其他的一些非原子操作提供原子操作的支持呢??
關鍵就在于Interlocked.CompareExchange 中,Jeffrey Richter把它叫做InterLocked Anything 模式。
下面我們使用Interlocked.CompareExchange 實現求最大值的原子操作。
do
{
startVal = currentVal; //將currentVal中的值保存到startVal中,此時記錄的是target在操作開始前的最初值。
desiredVal = Math.Max(startVal, value); //通過startVal進行復雜的計算,返回一個期望的結果,在這里僅僅是返回兩者的最大值。
//線程可能在這里被搶占,target的值可能被改變
//如果target的值被改變了,那么target和startVal的值就不想等,所以就不應該用desiredVal替換target.
//如果target的值沒有被改變,那么target和startVal的值就像等,使用desiredVal替換target.
//不管替換或者不替換,CompareExchange的返回值始終是target的值,所以currentVal的值現在是target的最新值。
//CompareExchange:將target和startVal的值比較,相等則用desiredVal替換,否則不操作,
//不管替換還是不替換返回的都是原來保存在target的值。
currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);
} while (startVal != currentVal); //當target的起始值和最新值不相等的時候,說明target被修改了,所以繼續下次判斷,否則退出循環。
return desiredVal;
}
在這里,計算可能會比較復雜,而不像上面的Math.Max一樣,所以可以使用委托調用的方式進行封裝。
static TResult Morph<TResult, TArgument>(ref int target, TArgument argument,
Morpher<TResult, TArgument> morpher)
{
TResult morphResult;
int currentVal = target, startVal, desiredVal;
do
{
startVal = currentVal;
desiredVal = morpher(startVal, argument, out morphResult);
currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);
} while (startVal != currentVal);
return morphResult;
}
新聞熱點
疑難解答