Interlocked.Increment 方法 和Interlocked.Decrement 方法作用

weixin_34304013發表於2011-01-06

Interlocked.Increment 方法:讓++成為原子操作;Interlocked.Decrement 方法讓--成為原子操作。
什麼叫原子操作呢。就是不會被別人打斷,因為C#中的一個語句,編譯成機器程式碼後會變成多個語句。
在多執行緒環境中,執行緒切換有可能會發生在這多個語句中間。使用Interlocked.Increment,Interlocked.Decrement 可以避免被打斷,保證執行緒安全。

使用Interlocked.Increment 方法和Interlocked.Decrement 方法MSND例子:

using System;
using System.Threading;

class Test
{
    static void Main()
    {
        Thread thread1 = new Thread(new ThreadStart(ThreadMethod));
        Thread thread2 = new Thread(new ThreadStart(ThreadMethod));
        thread1.Start();
        thread2.Start();
        thread1.Join();
        thread2.Join();

        // Have the garbage collector run the finalizer for each
        // instance of CountClass and wait for it to finish.
        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("UnsafeInstanceCount: {0}" +
            "\nSafeCountInstances: {1}",
            CountClass.UnsafeInstanceCount.ToString(),
            CountClass.SafeInstanceCount.ToString());
    }

    static void ThreadMethod()
    {
        CountClass cClass;
       
        // Create 100,000 instances of CountClass.
        for(int i = 0; i < 100000; i++)
        {
            cClass = new CountClass();
        }
    }
}

class CountClass
{
    static int unsafeInstanceCount = 0;//不使用原子操作
    static int   safeInstanceCount = 0;//使用原子操作

    static public int UnsafeInstanceCount
    {
        get {return unsafeInstanceCount;}
    }

    static public int SafeInstanceCount
    {
        get {return safeInstanceCount;}
    }

    public CountClass()
    {
        unsafeInstanceCount++;
        Interlocked.Increment(ref safeInstanceCount);
    }

    ~CountClass()
    {
        unsafeInstanceCount--;
        Interlocked.Decrement(ref safeInstanceCount);
    }
}

不用原子操作例子

class Program
    {
       
static void Main(string[] args)
        {
           
for (int loop = 0; loop < 20; loop++)
            {
                sum
= 0;
                Thread t1
= new Thread(Thread1);
                Thread t2
= new Thread(Thread2);
                t1.Start();
                t2.Start();

                t1.Join();
                t2.Join();
                Console.WriteLine(
"sum = " + sum);         // sum = 200000 ?
            }
        }

       
static int sum;
       
static void Thread1()
        {
           
for (int i = 0; i < 100000; i++) sum++;
        }
       
static void Thread2()
        {
           
for (int i = 0; i < 100000; i++) sum++;
        }
    }

結果:

/*
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 192361
sum = 175155
sum = 200000
sum = 176024
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 200000
sum = 176322
*/
Why the sum is not always 200000?
The reason is that sum++ is not thread safe (see the possible problem).
That is the reason we need Interlocked.Increment(), which guarantees the sum is always 200000.

Thread1 (sum++)                   Thread2 (sum++)
--------------------------------------------------------------------
mov   EAX, dword ptr sum          .
inc   EAX                         .
.                                 mov   EAX, dword ptr sum          
// load sum into a register
.                                 inc   EAX                          // increase it
.                                 mov   dword ptr sum, EAX           // save back
mov   dword ptr sum, EAX
--------------------------------------------------------------------

problem: two sum
++ are called in different thread,
but the sum
is incremented only once.
也就是說因為C#中的一個語句,編譯成機器程式碼後會變成多個語句,執行緒不安全,sum++的第100次操作就被打斷了,而在第200000次++操作結束後CPU才輪詢到sum++的第100次操作,這時sum的值就是101,



相關文章