深入多線程之:深入分析Interlocked
在大多數(shù)計(jì)算機(jī)上,增加變量操作不是一個(gè)原子操作,需要執(zhí)行下列步驟:
一:將實(shí)例變量中的值加載到寄存器中。
二:增加或減少該值。
三:在實(shí)例變量中存儲(chǔ)該值。
在多線程環(huán)境下,線程會(huì)在執(zhí)行完前兩個(gè)步驟后被搶先。然后由另一個(gè)線程執(zhí)行所有三個(gè)步驟,當(dāng)?shù)谝粋€(gè)線程重新開始執(zhí)行時(shí),它覆蓋實(shí)例變量中的值,造成第二個(gè)線程執(zhí)行增減操作的結(jié)果丟失。
Interlocked可以為多個(gè)線程共享的變量提供原子操作。
Interlocked.Increment:以原子操作的形式遞增指定變量的值并存儲(chǔ)結(jié)果。
Interlocked.Decrement以原子操作的形式遞減指定變量的值并存儲(chǔ)結(jié)果。
Interlocked.Add以原子操作的形式,添加兩個(gè)整數(shù)并用兩者的和替換第一個(gè)整數(shù)
但是Interlocked并沒(méi)有為乘法,除法提供原子操作。那么如何實(shí)現(xiàn)乘法,除法,以及為其他的一些非原子操作提供原子操作的支持呢??
關(guān)鍵就在于Interlocked.CompareExchange 中,Jeffrey Richter把它叫做InterLocked Anything 模式。
下面我們使用Interlocked.CompareExchange 實(shí)現(xiàn)求最大值的原子操作。
public static int Maximum(ref int target, int value)
{
int currentVal = target; //將target的當(dāng)前值保存到currentVal中
int startVal, desiredVal; //聲明兩個(gè)變量來(lái)記錄操作開始前的值和期望的結(jié)果值。
do
{
startVal = currentVal; //將currentVal中的值保存到startVal中,此時(shí)記錄的是target在操作開始前的最初值。
desiredVal = Math.Max(startVal, value); //通過(guò)startVal進(jìn)行復(fù)雜的計(jì)算,返回一個(gè)期望的結(jié)果,在這里僅僅是返回兩者的最大值。
//線程可能在這里被搶占,target的值可能被改變
//如果target的值被改變了,那么target和startVal的值就不想等,所以就不應(yīng)該用desiredVal替換target.
//如果target的值沒(méi)有被改變,那么target和startVal的值就像等,使用desiredVal替換target.
//不管替換或者不替換,CompareExchange的返回值始終是target的值,所以currentVal的值現(xiàn)在是target的最新值。
//CompareExchange:將target和startVal的值比較,相等則用desiredVal替換,否則不操作,
//不管替換還是不替換返回的都是原來(lái)保存在target的值。
currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);
} while (startVal != currentVal); //當(dāng)target的起始值和最新值不相等的時(shí)候,說(shuō)明target被修改了,所以繼續(xù)下次判斷,否則退出循環(huán)。
return desiredVal;
}
這段代碼的核心就是:currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);
// 將target的值和startVal的值比較,相等則用desiredVal替換target,否則不操作,
//不管替換還是不替換返回的都是原來(lái)保存在target的值。
在這里,計(jì)算可能會(huì)比較復(fù)雜,而不像上面的Math.Max一樣,所以可以使用委托調(diào)用的方式進(jìn)行封裝。
delegate int Morpher<TResult, TArgument>(int startValue, TArgument argument,
out TResult morphResult);
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;
}
基本原理和上面的一致。
相關(guān)文章
C#解析json字符串總是多出雙引號(hào)的原因分析及解決辦法
json好久沒(méi)用了,今天在用到j(luò)son的時(shí)候,發(fā)現(xiàn)對(duì)字符串做解析的時(shí)候總是多出雙引號(hào),下面給大家介紹C#解析json字符串總是多出雙引號(hào)的原因分析及解決辦法,需要的朋友參考下吧2016-03-03C# 修改文件的創(chuàng)建、修改和訪問(wèn)時(shí)間的示例
這篇文章主要介紹了C#實(shí)現(xiàn)修改文件的創(chuàng)建、修改和訪問(wèn)時(shí)間的示例,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-04-04Unity3D運(yùn)行報(bào)DllNotFoundException錯(cuò)誤的解決方案
這篇文章主要介紹了Unity3D運(yùn)行報(bào)DllNotFoundException錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04C#驗(yàn)證身份證號(hào)碼正確性的實(shí)例代碼(收藏)
這篇文章主要介紹了C#驗(yàn)證身份證號(hào)碼正確性的實(shí)例代碼,包括18位號(hào)碼和15位號(hào)碼的校驗(yàn),需要的朋友可以參考下2017-07-07C# 使用CancellationTokenSource取消多線程
有時(shí)間我們?cè)谑褂枚嗑€程的時(shí)候,需要取消線程的執(zhí)行,可以使用CancellationTokenSource來(lái)取消對(duì)Task開辟多線程的取消,感興趣的可以了解一下2021-08-08