雖然線程可以在一定程度上提高程序運行的效率,但也會產生一些副作用。讓 我們先看看如下的代碼:
class Increment
{
private int n = 0;
private int max;
public Increment(int max)
{
this.max = max;
}
public int result
{
get
{
return n;
}
set
{
n = value;
}
}
public void Inc()
{
for (int i = 0; i < max; i++)
{
n++;
}
}
}
class Program
{
public static void Main()
{
Increment inc = new Increment(10000);
Thread[] threads = new Thread[30];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(inc.Inc);
threads[i].Start();
}
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join(); // 等待30個線程都執行完
}
Console.WriteLine(inc.result); //輸出n的值
}
}
上面的程序的基本功能是使用Increment的Inc方法為n遞 增max,所不同的是,將在Main方法中啟動30個線程同時執行Inc方法。在本例中 max的值是10000(通過Increment的構造方法傳入)。讀者可以運行一下這個程序 ,正常的結果應該是300000,但通常不會得到這個結果,一般獲得的結果都比 300000小。其中的原因就是Inc方法中的n++上,雖然從表面上看,n++只是一條簡 單的自增語言,但從底層分析,n++的IL代碼如下:
ldsfld// 獲得n的初 始值,並壓到方法棧中
ldc.i4.1// 將1壓到方法棧中
add// 從方 法棧中彈出最頂端的兩個值,相加,然後將結果保存在方法棧中
stfld// 從當前方法棧中彈出一個值,並更新類字段n
對於上面每一條IL語句是線 程安全的,但是n++這條C#語句需要上面的四步才能完成,因此,n++這條語句並 不是線程安全的。只要在執行stfld指令之前的任何一步由於其他線程獲得CPU而 中斷,那麼就會出現所謂的“髒”數據。