1. 程式人生 > >C# 原子操作理解

C# 原子操作理解

turn 其他 暫時 ram result sta 最大值 兩個 變量

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

C# 原子操作理解