InterLockedIncrement

InterLockedIncrement是一個函式,他的功能是實現數的原子性加減。

函式原型

自加鎖函式定義:

LONG InterLockedIncrement(
LPLONG lpAddend // variable address
);

LONG InterlockedDecrement(
LPLONG lpAddend // variable address
);

函式功能

InterLockedIncrement andInterLockedDecrement

實現數的原子性加減。什麼是原子性的加減呢?

舉個例子:如果一個變數 Long value =0;

首先說一下正常情況下的加減操作:value+=1;

1:系統從Value的空間取出值,並動態生成一個空間來存儲取出來的值;

2:將取出來的值和1作加法,並且將和放回Value的空間覆蓋掉原值。加法結束。

如果此時有兩個Thread ,分別記作threadA,threadB。

1:threadA將Value從存儲空間取出,為0;

2:threadB將Value從存儲空間取出,為0;

3:threadA將取出來的值和1作加法,並且將和放回Value的空間覆蓋掉原值。加法結束,Value=1。

4:threadB將取出來的值和1作加法,並且將和放回Value的空間覆蓋掉原值。加法結束,Value=1。

最後Value =1 ,而正確應該是2;這就是問題的所在,InterLockedIncrement 能夠保證在一個執行緒訪問變數時其它執行緒不能訪問。同理InterLockedDecrement。

LONG InterlockedDecrement(

LPLONG lpAddend // variable address

);

屬於互鎖函式,用在同一進程內,需要對共享的一個變數,做減法的時候,

防止其他執行緒訪問這個變數,是實現執行緒同步的一種辦法(互鎖函式)

首先要理解多執行緒同步,共享資源(同時訪問全局變數的問題),否則就難以理解。

result = InterlockedDecrement(&SomeInt)

如果不考慮多執行緒其實就是 result = SomeInt - 1;

但是考慮到多執行緒問題就複雜了一些。就是說如果想要得到我預期的結果並不容易。

result = SomeInt - 1;

舉例說:

SomeInt如果==1;

預期的結果result當然==0;

但是,如果SomeInt是一個全程共享的全局變數情況就不一樣了。

C語言的"result = SomeInt - 1;"

在實際的執行過程中,有好幾條指令,在指令執行過程中,其它執行緒可能改變SomeInt值,使真正的結果與你預期的不一致。

所以InterlockedDecrement(&SomeInt)的執行過程是這樣的

{

__禁止其他執行緒訪問 (&SomeInt) 這個地址

SomeInt --;

move EAX, someInt; // 設定返回值,C++函式的返回值 都放在EAX中,

__開放其他執行緒訪問 (&SomeInt) 這個地址

}

但是實際上只需要幾條指令加前綴就可以完成,以上說明是放大的。

你也許會說,這有必要嗎? 一般來說,發生錯誤的機率不大,但是防範總是必要的

示例

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,

與之相對應的自減鎖函式為:

相關詞條

相關搜尋

熱門詞條

聯絡我們