【问题标题】:Parallel.For and Parallel.ForEach not running to conclusionParallel.For 和 Parallel.ForEach 没有得出结论
【发布时间】:2014-02-12 08:07:55
【问题描述】:

这是一个失败的测试。如何确认循环运行的次数正确?

    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);
    }

【问题讨论】:

  • How can I confirm that the loops run the correct number of times? 它们运行的​​次数正确,但是您对共享对象的访问不受控制。

标签: c# task-parallel-library parallel.foreach


【解决方案1】:

同时,您正在运行这些行:

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

对于任何特定的val 值,不能保证只有一个线程/任务同时运行这些行。所以两个线程可以读取值 2,加 1,并存储值 3。您需要使用线程安全的方法来累积总数。

例如您可以使用 Parallel.For 的重载,它允许每个线程分别构建自己的结果副本,然后有一个最终的组合步骤来允许您计算总结果:

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

【讨论】:

    【解决方案2】:

    您正在使用非线程安全的哈希表实现。因此,您只能证明您犯了错误。请改用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);
    });
    

    【讨论】:

      【解决方案3】:

      您可以使用信号量来序列化并发访问 resultsParallel、resultsParallelForEach:

      公共类示例 { 公共静态随机随机器{得到;放; } 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);
          }
      } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-16
        相关资源
        最近更新 更多