2013年7月26日 星期五

Atomic Operations


原子運算指的是不會被切割的運算 (可以想像成在進行原子運算時,系統偷偷幫我們上了鎖)。舉個例子,下面這支程式裡,count++ 並不是原子運算,因此最後顯示出來的結果不一定會是 2000000:

using System;
using System.Threading;

namespace ConsoleApplication2 {
    class Program {
        static Int32 count = 0;
        static void Main(string[] args) {
            const Int32 REPEAT = 1000000;
            Thread th1 = new Thread(() => {    
                for(Int32 i = 0; i < REPEAT; i++) count++;
            });
            Thread th2 = new Thread(() => {
                for(Int32 i = 0; i < REPEAT; i++) count++;
            });
            th1.Start();    th2.Start();
            th1.Join();     th2.Join();
            Console.WriteLine(count);
            Console.ReadKey();
        }
    }
}

下面這支程式裡,Interlocked.Increment(ref count) 是原子運算,因此最後顯示出來的結果一定會是 2000000:

using System;
using System.Threading;

namespace ConsoleApplication2 {
    class Program {
        static Int32 count = 0;
        static void Main(string[] args) {
            const Int32 REPEAT = 1000000;
            Thread th1 = new Thread(() => {
                for(Int32 i = 0; i < REPEAT; i++) Interlocked.Increment(ref count);

            });
            Thread th2 = new Thread(() => {
                for(Int32 i = 0; i < REPEAT; i++) Interlocked.Increment(ref count);
            });
            th1.Start();    th2.Start();
            th1.Join();     th2.Join();
            Console.WriteLine(count);
            Console.ReadKey();
        }
    }
}

C# 規格書(C# Specification)裡,明白的規定了下面事項:

12.5 Atomicity of variable references 

Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.

翻譯:
以下的型別變數的【讀取】與【寫入】保證是原子運算: bool, char, byte, sbyte, short, ushort, uint, int, float, 以及 reference types。另外,以上述型別為基底的 enum types 其【讀取】與【寫入】也保證是原子運算。其他型別的【讀取】與【寫入】則不保證為原子運算。【讀-改-寫】 類型的運算,例如 ++、--、+=、-= 之類的,不保證為原子運算。

Int32 i = 3; // 原子運算
Int64 j = 3; // 不保證為原子運算
Int32 k += i; // 不保證為原子運算
i++; // 不保證為原子運算
Object o1 = null; // 原子運算
Object o2 = o1; // 原子運算

以下是一個非原子運算所導致的競走問題。把 i64 的型別改為 UInt32 就會好。

using System;
using System.Threading;

namespace ConsoleApplication2 {
    class Program {
        static UInt64 i64 = 0;
        static void Main(string[] args) {
            Thread th1 = new Thread(() => {                
                while(true) i64 = 0;
            });
            Thread th2 = new Thread(() => {
                while(true)
                    i64 = 0xFFFFFFFFFFFFFFFFul;
                ;
            });
            th1.Start();    th2.Start();
            UInt32 good = 0, bad = 0;
            while(true) {
                UInt64 tmp = i64;
                if(tmp != 0 && tmp != 0xFFFFFFFFFFFFFFFFul) {
                    bad++;
                    Console.WriteLine("Opps good(" + good.ToString() + 
                        ") bad(" + (bad).ToString() + ")");
                } else {
                    good++;
                }
            }
        }
    }
}

沒有留言:

張貼留言