上次我們講到了集合。說到集合,那往往少不了循環。今天我們說下什麼是并行循環
Parallel.For,Paraller.Foreach
(System.Threading.Tasks)
相信大家對此都不陌生。
long sum = 0;
Parallel.For(0, MaxValue, (i) =>
{
Interlocked.Add(ref sum, (long)Math.Sqrt(i));
values.Add(i);
});
也可以Paraller.Foreach.
中斷循環:
如果需要中斷循環,可以給循環的委托傳入一個ParallelLoopState對象,中止循環。有兩個方法。
Break,Stop;差別是什麼呢?
1:Break---告訴循環不要執行大于目前疊代次數的任何疊代。啥意思呢。就是大于i 的都會停止運作,小于i的依然會執行。注意:可能會有多個循環疊代發起Break調用,要看代碼邏輯了。
2:Stop:----停止任何疊代。
問題調整
~對了。在使用并行循環的時候,要確定每次疊代的工作量要明顯大于同步共享狀态的開銷。如果你把循環的時間都耗在阻塞共享的循環變量上,那并行執行也就沒意義了。是以每次疊代都進行局部通路,
~并行循環還有另一個問題,就是委托。每次都會生成一個委托,如果每次疊代完成的工作還不如生成委托的開銷大。那就是殺雞用牛刀。大材小用。
說下委托的開銷吧。委托的開銷分為兩種:構造開銷和調用開銷,調用和普通方法調用差不多。構造開銷就很大。你應該隻做一次構造,并把對象緩存起來。把委托定義在循環外。
上面的問題怎麼解決呢?
用Partitioner類,它會把需要的疊代的區間分拆存入Tuple對象種。
public static void Main()
{
Stopwatch watch=new Stopwatch();
const int MaxValue=10000000;
long sum=0;
//普通循環
watch.Restart();
sum=0;
Parallel.For(0,MaxValue,(i)=>{
Interlocked.Add(ref sum,(long)Math.Sqrt(i));
});
watch.Stop();
Console.WriteLine("Parallel.For:{0}",watch.Elapsed);
//分區的For循環
var partitioner=Partitioner.Create(0,MaxValue);
Parallel.ForEach(partitioner,(range)=>{
long partialSum=0;
for(int i=range.Item1;i<range.Item2;i++)
{
partialSum+=(long)Math.Sqrt(i);
}
Interlocked.Add(ref sum,partialSum);
Console.WriteLine("Partitioned Parallel.For:{0}",watch.Elapsed);
}
我的電腦上執行的結果。
上面分區規則是靜态的,隻要疊代區間劃分完畢,每個區間都運作一個委托。其中有一個提前完成,也不會嘗試重新分區。