【问题标题】:Code seems to move on prior to Parallel.For loops finishing代码似乎在 Parallel.For 循环完成之前继续前进
【发布时间】:2021-10-19 15:53:31
【问题描述】:

我正在努力使用Parallel.For 循环。我可以说他们确实加快了我长时间运行的代码,但我得到了空对象错误,就好像代码在完成并行循环之前移动了一样。下面是我尝试过的带有注释掉的 Parallel.For 语句的代码。

public bool Calculate()
{
    // make list of all possible flops 22100
    List<Flop> flops = new List<Flop>();
    CardSet deck = new CardSet();
    for (int i = 0; i < deck.Size() - 2; i++)
    {
        SbCard card1 = deck.GetCard(i);
        for (int j = i + 1; j < deck.Size() - 1; j++)
        {
            SbCard card2 = deck.GetCard(j);
            for (int k = j + 1; k < deck.Size(); k++)
            {
                SbCard card3 = deck.GetCard(k);
                flops.Add(new Flop(card1, card2, card3));
            }
        }
    }

    int progress = 0;
    var watch = System.Diagnostics.Stopwatch.StartNew();

    // Loop over each flop
    //Parallel.For(0, flops.Count, i =>
    for (int i = 0; i < flops.Count; i++)
    {
        Dictionary<FlopEquityHoldemHandPair, FlopEquityHoldemHandPair> flopPairs =
        new Dictionary<FlopEquityHoldemHandPair, FlopEquityHoldemHandPair>();
        Flop flop = flops[i];
        String filePath = Directory.GetCurrentDirectory() + "\\flops\\" +
        flop.GetSorted() + ".txt";

        if (!File.Exists(filePath))
        {
            // make list of all available starting hands
            List<HoldemHand> hands = new List<HoldemHand>();
            deck = new CardSet();
            deck.RemoveAll(flop);
            for (int j = 0; j < deck.Size() - 1; j++)
            {
                SbCard card1 = deck.GetCard(j);
                for (int k = j + 1; k < deck.Size(); k++)
                {
                    SbCard card2 = deck.GetCard(k);
                    hands.Add(new HoldemHand(card1, card2));
                }
            }

            // loop over all hand vs hand combos
            //Parallel.For(0, hands.Count - 1, j =>
            for (int j = 0; j < hands.Count - 1; j++)
            {
                HoldemHand hand1 = hands[j];
                //Parallel.For(j + 1, hands.Count, k =>
                for (int k = j + 1; k < hands.Count; k++)
                {
                    HoldemHand hand2 = hands[k];
                    if (!hand1.Contains(hand2))
                    {
                        FlopEquityHoldemHandPair holdemHandPair = new
                          FlopEquityHoldemHandPair(hand1, hand2);
                        if (!flopPairs.ContainsKey(holdemHandPair))
                        {
                            // next line triggers a loop of 1980 iterations
                            flopPairs.Add(holdemHandPair, new
                            FlopEquityHoldemHandPair(new
                            EquityHoldemHand(hand1), new
                            EquityHoldemHand(hand2), flop));
                        }
                    }
                }//);
            }//);


            // WRITE FILE FOR CURRENT FLOP
            StringBuilder sb = new StringBuilder();
            foreach (FlopEquityHoldemHandPair pair in flopPairs.Values)
            {
                // Null value appears in flopPairs.Values and the list of values is around 200 short of the 600k values it should have
                sb.AppendLine(pair.ToString());
            }
            File.WriteAllText(filePath, sb.ToString());

            // reports calculation progress 1% at a time
            int num = ((int)(i * 100 / 22100));
            if (num > progress)
            {
                progress = num;
                Console.WriteLine("Progress: " + progress + "%");
            }
        }
    }//);

    watch.Stop();
    var elapsedMs = watch.ElapsedMilliseconds;

    Console.WriteLine("Finished in " + elapsedMs / 60000 + "mins");
    return true;
}

当我在这段代码的后期到达foreach 循环时,flopPairs 中有一些项目。值为 null 并且字典没有应有的那么大 - 就好像在代码继续之前某些计算没有完成一样。我很抱歉,如果没有更多代码,这段代码就无法运行,但有很多东西可以提供。如果问题对某人来说不是很明显,我可以尝试提供一个最小的简化示例。

【问题讨论】:

  • 根据这个答案(stackoverflow.com/questions/10153729/…)paralell foreach 应该阻塞,直到循环的最后一部分完成。 |请在注释中指定您获得 Null 值的行。
  • 它是 Parallel For 而不是 Parallel Foreach。不确定这是否会有所作为。我在出现空值的代码中添加了注释
  • 我有两个建议给你。一是避免嵌套Parallel 循环。对于大多数工作负载来说,一个Parallel 循环就足够了。将一个嵌套在另一个中只会增加开销。另一个建议是在尝试编写多线程程序之前系统地研究线程。您必须熟悉线程同步、竞争条件、内存屏障等概念。要学习的东西很多,仅凭直觉和完全缺乏是不可能成功的。这是一个有价值的在线资源:Threading in C#,作者 Joseph Albahari。
  • Dictionary 不是线程安全的,因此时髦的数据不足为奇。考虑使用线程安全集合,如ConcurrentDictionary
  • @JohnWu 谢谢 - 这似乎解决了我的问题。不知道字典是导致问题的原因

标签: c# multithreading for-loop parallel-processing parallel.for


【解决方案1】:

正如 John Wu 在 cmets 中所说,Dictionary 不是线程安全的,导致了我的问题。使用 ConcurrentDictionary 是正确的答案

【讨论】:

    猜你喜欢
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-02-15
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多