2014-02-12 35 views
0

這是失敗的測試。我如何確認循環運行正確的次數?Parallel.For和Parallel.ForEach沒有運行到結論

public Random Randomator { get; set; } 
    public const int TimesToRun = 1000000; 

    [TestMethod] 
    public void ThrowTheDice() 
    { 
     Randomator = new Random(); 

     var resultsParallel = new Dictionary<int, int> 
     { 
      {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0} 
     }; 

     var resultsParallelForEach = new Dictionary<int, int> 
     { 
      {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0} 
     }; 

     var stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     Parallel.For(0, TimesToRun, ctr => 
     { 
      var val = ThrowDice(); 
      if (!resultsParallel.ContainsKey(val)) 
       throw new ArgumentOutOfRangeException(); 

      var existing = resultsParallel[val]; 
      resultsParallel[val] = existing + 1; 
     }); 

     stopwatch.Stop(); 
     var parallelTime = stopwatch.Elapsed; 

     stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     var numbers = Enumerable.Range(0, TimesToRun); 
     Parallel.ForEach(numbers, ctr => 
     { 
      var val = ThrowDice(); 
      if (!resultsParallel.ContainsKey(val)) 
       throw new ArgumentOutOfRangeException(); 

      var existing = resultsParallelForEach[val]; 
      resultsParallelForEach[val] = existing + 1; 
     }); 

     stopwatch.Stop(); 
     var parallelForEachTime = stopwatch.Elapsed; 

     var parallelTotal = resultsParallel.Sum(x => x.Value); 
     var parallelForEachTotal = resultsParallelForEach.Sum(x => x.Value); 

     Assert.AreEqual(parallelTotal, TimesToRun); 
     Assert.AreEqual(parallelForEachTotal, TimesToRun); 
    } 

    public int ThrowDice() 
    { 
     return Randomator.Next(1, 7); 
    } 
+0

'我如何確認循環運行正確次數?'它們運行的​​次數正確,但是您可以訪問未受控制的共享對象。 –

回答

7

與此同時,你正在運行這些行:

var existing = resultsParallel[val]; 
resultsParallel[val] = existing + 1; 

有沒有保證只有一個線程/任務運行在同一時間的線路,對於任何特定val值。因此,兩個線程可以讀取值2,添加1並存儲值3.您需要使用線程安全方法來累計總計。

E.g.你可以使用的Parallel.For過載,使每個線程建立自己單獨的結果的副本,然後有一個最終的合併步驟,讓你計算總成績:

public static ParallelLoopResult For<TLocal>(
    long fromInclusive, 
    long toExclusive, 
    Func<TLocal> localInit, 
    Func<long, ParallelLoopState, TLocal, TLocal> body, 
    Action<TLocal> localFinally 
) 
1

您使用哈希表實現,不是線程安全的。因此,你只能證明你犯了一個錯誤。使用ConcurrentDictionary相反,它是線程安全的:

var resultsParallel = new ConcurrentDictionary<int, int>(); 

var stopwatch = new Stopwatch(); 
stopwatch.Start(); 
Parallel.For(0, TimesToRun, ctr => 
{ 
    var val = ThrowDice(); 
    resultsParallel.AddOrUpdate(val, 1, (key, old) => old + 1); 
}); 
0

你可以使用一個信號燈序列化到resultsParallel併發訪問,resultsParallelForEach:

公共類實例 { 公共靜態隨機Randomator {獲得;組; } public const int TimesToRun = 1000000;

public static Semaphore semaphore; 


    public static void ThrowTheDice() 
    { 
     Randomator = new Random(); 

     semaphore = new Semaphore(1, 1); 

     var resultsParallel = new Dictionary<int, int> 
    { 
     {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0} 
    }; 

     var resultsParallelForEach = new Dictionary<int, int> 
    { 
     {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0} 
    }; 

     var stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     Parallel.For(0, TimesToRun, ctr => 
     { 
      var val = ThrowDice(); 
      if (!resultsParallel.ContainsKey(val)) 
       throw new ArgumentOutOfRangeException(); 

      semaphore.WaitOne(); 

      var existing = resultsParallel[val]; 
      resultsParallel[val] = existing + 1; 

      semaphore.Release(); 
     }); 

     stopwatch.Stop(); 
     var parallelTime = stopwatch.Elapsed; 

     stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     var numbers = Enumerable.Range(0, TimesToRun); 
     Parallel.ForEach(numbers, ctr => 
     { 
      var val = ThrowDice(); 
      if (!resultsParallel.ContainsKey(val)) 
       throw new ArgumentOutOfRangeException(); 

      semaphore.WaitOne(); 

      var existing = resultsParallelForEach[val]; 
      resultsParallelForEach[val] = existing + 1; 

      semaphore.Release(); 
     }); 

     stopwatch.Stop(); 
     var parallelForEachTime = stopwatch.Elapsed; 

     var parallelTotal = resultsParallel.Sum(x => x.Value); 
     var parallelForEachTotal = resultsParallelForEach.Sum(x => x.Value); 

     Debug.Assert(parallelTotal == TimesToRun); 
     Debug.Assert(parallelForEachTotal == TimesToRun); 
    } 

    public static int ThrowDice() 
    { 
     return Randomator.Next(1, 7); 
    } 
}