C# 原子操作理解


C#內置提供的原子操作

  1. Interlocked.Increment:以原子操作的形式遞增指定變量的值並存儲結果。
  2. Interlocked.Decrement:以原子操作的形式遞減指定變量的值並存儲結果。
  3. Interlocked.Add:以原子操作的形式,添加兩個整數並用兩者的和替換第一個整數

問題:如果要進行原子的乘法、除法或者其他操作改怎么辦,C#並沒有內置提供相應的方法呀?

那我們先來大概理解一下原子操作的流程

以增加變量值為例

  1. 將實例變量添加到CPU寄存器中
  2. 將該變量的值進行增加
  3. 將該變量的增加后的值從CPU寄存器中還原到堆或棧中實例變量的值

可以看到如果是多核CPU在多線程環境下可能會導致在執行完步驟1,步驟2后當前線程失去時間片,其他線程讀取改變量的值時就會讀取到並未增加增加之前的值,所以就導致了數據的不一致。然而C#提供了上面三種原子操作來保證不出現這樣的數據不一致,至於底層原理暫時不深究,我們來看看如何實現除這三種方法之外的操作。

進行原子的獲取最大值操作

先放代碼如下:

  public static int Maximum(ref int target, int value)
        {   
            //注意target前面加了 ref ,這樣在方法外改變target的值將會影響到方法內的target值,
            //即類似按引用傳遞
            int currentVal = target;//使用currentVal局部變量來存儲target值,target值的變更不會影響currentVal
            int startVal = 0;       
            int desiredVal = 0;

            do
            {
                startVal = currentVal; //記錄本次迭代的起始值   1
                desiredVal = Math.Max(startVal, value);//根據startVal和value計算最大值desiredVal    2
                //如果target和startVal值一致則用desiredVal賦值給target,並將target原始值進行返回
                //如果target和startVal值不相同則什么都不做,並將target的值進行返回
                //target的值因為在別的線程進行操作時可能改變target的值,所以導致target值和startVal不一致
                currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);//  3
            } while (startVal != currentVal); // 4
            //因為startVal記錄的是開始時target的值,而currentVal記錄的則是target最新值
            //如果startVale和currentVal不一致則代表其他線程已經更改的target的值,所以需要重新迭代。
            //重新迭代時currentVal代表的是target的最新值
            //疑問:如果在 3 之后 4之前 target的值被其他線程更改怎么辦?請大神幫忙
            return desiredVal;
        }

大致的邏輯可以參考注釋進行理解,最主要的方法是通過currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal); 可以參考MSDN此方法的解釋

其他操作

C# VIA CLR中給出了其他操作的模板,可以參考如下:

    
        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;
            int startVal = 0;
            int desiredVal = 0;
            do
            {
                startVal = currentVal;
                desiredVal = morpher(startVal, argument, out morphResult);
                currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);
            } while (startVal != currentVal);
            return morphResult;

        }

可以通過委托定義自己想要的操作。乘法、除法等等。
大神寫出的書就是牛,膜拜Jeffrey Richter


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM