我有以下兩種方法的代碼:
using System.Diagnostics;
internal class Program
{
public static double SeqArraySum(double[] x)
{
var watch = new Stopwatch();
watch.Start();
double sum1 = 0;
double sum2 = 0;
for (int i = 0; i < x.Length / 2; i )
sum1 = x[i];
for (int i = x.Length / 2; i < x.Length; i )
sum2 = x[i];
double sum = sum1 sum2;
watch.Stop();
Console.WriteLine($"Seq Milliseconds: {watch.ElapsedMilliseconds}, sum:{sum}");
return sum;
}
public static double ParArraySum(double[] x)
{
var watch = new Stopwatch();
watch.Start();
double sum1 = 0;
double sum2 = 0;
var t1 = new Thread(() =>
{
for (int i = 0; i < x.Length / 2; i )
sum1 = x[i];
});
t1.Start();
for (int i = x.Length / 2; i < x.Length; i )
sum2 = x[i];
t1.Join();
double sum = sum1 sum2;
watch.Stop();
Console.WriteLine($"Par Milliseconds: {watch.ElapsedMilliseconds}, sum:{sum}");
return sum;
}
static void Main()
{
var arr = new double[100000000];
for (int i = 0; i < arr.Length; i )
arr[i] = i / 100000000000d;
for(int i = 1; i <= 5; i )
{
Console.WriteLine($"Attempt {i}");
var resultSeq = SeqArraySum(arr);
var resultPar = ParArraySum(arr);
}
}
}
如果我運行這個程式,我會得到以下結果(.NET Core 6.0,除錯模式):
Attempt 1
Seq Milliseconds: 226, sum:49999,999499999976
Par Milliseconds: 448, sum:49999,999499999976
Attempt 2
Seq Milliseconds: 194, sum:49999,999499999976
Par Milliseconds: 409, sum:49999,999499999976
Attempt 3
Seq Milliseconds: 194, sum:49999,999499999976
Par Milliseconds: 463, sum:49999,999499999976
Attempt 4
Seq Milliseconds: 195, sum:49999,999499999976
Par Milliseconds: 460, sum:49999,999499999976
Attempt 5
Seq Milliseconds: 192, sum:49999,999499999976
Par Milliseconds: 457, sum:49999,999499999976
我希望 ParArraySum 運行得更快,但它沒有發生。但如果更換
var t1 = new Thread(() =>
{
for (int i = 0; i < x.Length / 2; i )
sum1 = x[i];
});
有了這個:
var t1 = new Thread(() =>
{
var tempSum = 0d;
for (int i = 0; i < x.Length / 2; i )
tempSum = x[i];
sum1 = tempSum;
});
我得到了預期的加速。
Attempt 1
Seq Milliseconds: 217, sum:49999,999499999976
Par Milliseconds: 130, sum:49999,999499999976
Attempt 2
Seq Milliseconds: 241, sum:49999,999499999976
Par Milliseconds: 118, sum:49999,999499999976
Attempt 3
Seq Milliseconds: 199, sum:49999,999499999976
Par Milliseconds: 115, sum:49999,999499999976
Attempt 4
Seq Milliseconds: 191, sum:49999,999499999976
Par Milliseconds: 115, sum:49999,999499999976
Attempt 5
Seq Milliseconds: 192, sum:49999,999499999976
Par Milliseconds: 115, sum:49999,999499999976
為什么我的原始代碼這么慢?是否可以重復訪問 sum1 而不會減速?
uj5u.com熱心網友回復:
通過做這個:
double sum1 = 0;
var t1 = new Thread(() =>
{
for (int i = 0; i < x.Length / 2; i )
sum1 = x[i];
});
您在閉包中捕獲sum1區域變數。為了使其作業,編譯器必須重寫代碼,使其sum1不再是常規的區域變數,而是編譯器生成的類的欄位,然后將該類的實體傳遞給閉包(另一個執行緒運行的代碼)。
在這種情況下,這對于性能很重要,因為訪問存盤在堆疊上的“常規”區域變數在對大型陣列求和的情況下效率更高,并且編譯器可以使用各種優化。一遍又一遍地訪問某個實體的欄位效率較低。
在這種情況下:
var t1 = new Thread(() =>
{
var tempSum = 0d;
for (int i = 0; i < x.Length / 2; i )
tempSum = x[i];
sum1 = tempSum;
});
您在傳遞給新執行緒的方法內創建區域變數,因此它將是“常規”區域變數,并且重復訪問它是有效的。然后,您只能在回圈結束時訪問捕獲的變數sum1一次,這不會對性能產生負面影響。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/520381.html
標籤:C#多线程异步并发
上一篇:如何在C#中重用執行緒
下一篇:使用互斥鎖等待退出信號
