2014-02-12 6 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의 값을 저장할 수 있습니다. 합계를 누적하는 데 thread-safe 메소드를 사용해야합니다.

예. 각 스레드가 개별적으로 결과의 복사본을 구축 할 수 있도록 한 다음 전체 결과를 산출 할 수 있도록 최종 결합 단계가 Parallel.For의 과부하 사용할 수 있습니다 당신은 해시 테이블 구현을 사용하는

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

을 그 스레드로부터 안전하지 않습니다. 따라서, 당신은 단지 실수했다고 증명할뿐입니다. 당신은 resultsParallel에 동시 액세스를 직렬화하기 위해 세마포어를 사용할 수 있습니다

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

, resultsParallelForEach : thread 세이프 인 대신 ConcurrentDictionary를 사용

공용 클래스 예 { 공공 정적 임의 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); 
    } 
}