2013-08-22 73 views
7

所以話題就是問題。AsParallel擴展實際上如何工作

我得到的方法AsParallel返回包裝ParallelQuery<TSource>使用相同的LINQ的關鍵字,但是從System.Linq.ParallelEnumerable代替System.Linq.Enumerable

這是很清楚的,但是當我尋找到反編譯的來源,我不明白怎麼做有用。

讓我們從最簡單的擴展開始:Sum()方法。代碼:

[__DynamicallyInvokable] 
public static int Sum(this ParallelQuery<int> source) 
{ 
    if (source == null) 
    throw new ArgumentNullException("source"); 
    else 
    return new IntSumAggregationOperator((IEnumerable<int>) source).Aggregate(); 
} 

很明顯,我們來看看Aggregate()的方法。它是InternalAggregate方法的一個封裝,用於捕獲一些異常。現在我們來看看它。

protected override int InternalAggregate(ref Exception singularExceptionToThrow) 
{ 
    using (IEnumerator<int> enumerator = this.GetEnumerator(new ParallelMergeOptions?(ParallelMergeOptions.FullyBuffered), true)) 
    { 
    int num = 0; 
    while (enumerator.MoveNext()) 
     checked { num += enumerator.Current; } 
    return num; 
    } 
} 

這裏是問題:它是如何工作的?我發現變量沒有併發安全性,被許多線程修改,我們只看到迭代器和求和。它是魔法枚舉器嗎?或者它是如何工作的? GetEnumerator()返回QueryOpeningEnumerator<TOutput>,但它的代碼太複雜了。

回答

2

最後,在我的第二個PLINQ突擊我找到了答案。這很清楚。 問題是,調查員並不簡單。這是一個特殊的multithreading之一。那麼它是如何工作的?答案是enumerator不返回源的下一個值,它返回下一個分區的一個整數。因此,此代碼僅執行2,4,6,8倍(基於Environment.ProcessorCount),執行實際求和工作時內部enumerator.MoveNextenumerator.OpenQuery方法

所以TPL明確地分區源可枚舉,然後獨立地求和每個分區,然後pefrorm這個總和,見IntSumAggregationOperatorEnumerator<TKey>。這裏沒有魔法,只是可以更深入地探索。

1

Sum運算符聚合單個線程中的所有值。這裏沒有多線程。訣竅是多線程正在其他地方發生。

PLINQ Sum方法可以處理PLINQ枚舉。這些枚舉可以使用其他構造(例如where)來構建,這樣可以通過多個線程處理集合。

Sum運算符始終是鏈中的最後一個運算符。儘管可以在多個線程上處理這個總和,但TPL團隊可能發現這對性能有負面影響,這是合理的,因爲這種方法唯一要做的就是簡單的整數加法。

因此,此方法處理所有來自其他線程的結果,並在單個線程上處理它們並返回該值。真正的訣竅在於其他PLINQ擴展方法。

+1

如果「這裏沒有多線程」,爲什麼sum2計算比sum1快3倍? http://i.imgur.com/Z4CtyUz.png –

+0

@AlexJoukovsky:我測試了這個,'AsParallel'版本的確速度更快,但我不知道這怎麼可能,特別是因爲輸入枚舉也是在單個一次一個線程。離奇。 – Steven

+0

部分和可以並行處理,然後合併。 – usr

-2
​​

這段代碼不會並行執行,while將依次執行它的innerscope。

試試這個

 List<int> list = new List<int>(); 

     int num = 0; 

     Parallel.ForEach(list, (item) => 
      { 
       checked { num += item; } 
      }); 

內的行動將在線程池來傳播,當所有的項目都處理的foreach語句將完成。

這裏需要threadsafety:

 List<int> list = new List<int>(); 

     int num = 0; 

     Parallel.ForEach(list, (item) => 
      { 
       Interlocked.Add(ref num, item); 
      }); 
+3

是不是我的代碼,它是.Net Framework 4.5的反編譯源碼,它的工作原理** –

+0

Ofcourse它的工作原理,但它不是多線程的。它被轉換爲IEnumerable? _IntSumAggregationOperator((IEnumerable )source_所以仍然是按順序執行的。我認爲沒有任何理由Sum多線程,因爲它們都會影響結果。 –

+0

缺少拳擊不能成爲3x性能差異的原因 –