【问题标题】:Program hangs for no reason程序无故挂起
【发布时间】:2013-04-05 20:40:26
【问题描述】:

我正在使用 tpl 来并行执行数据。问题是有时它会无缘无故地挂起这样的输出:

The thread '<No Name>' (0x4aa4) has exited with code 0 (0x0).
The thread '<No Name>' (0x2bf4) has exited with code 0 (0x0).
The thread '<No Name>' (0x417c) has exited with code 0 (0x0).
The thread '<No Name>' (0x432c) has exited with code 0 (0x0).
The thread '<No Name>' (0x3ad0) has exited with code 0 (0x0).
The thread '<No Name>' (0x4440) has exited with code 0 (0x0).
The thread '<No Name>' (0x24e8) has exited with code 0 (0x0).
The thread '<No Name>' (0x3354) has exited with code 0 (0x0).
The thread '<No Name>' (0x4a30) has exited with code 0 (0x0).

该程序仍将执行,当我在 Visual Studio 中暂停它时,它会在我有 ForEach 的并行过程的地方暂停:

Parallel.ForEach
(
   hold1.remainingHolds.ToArray(), 
   subSequence =>
   {
      //only if subsequence has 1 common substring
      if (checkIfCommon(remainingSequence, subSequence.ToString()) == 1)
      {
         goDownLinkType2(subSequence.ToString().Split(','), 0, 
           (SuffixNode)hold1.childHolds[subSequence.ToString().Split(',')[0]]);
       }
    }
  );

我不认为它是死锁,因为不会有线程等待不同的资源并最终相互锁定

它应该进入这个方法内部并递归遍历一个后缀树,直到它会等到没有线程将任何对象添加到数组列表中

 Boolean flag = false;
 public void goDownLinkType2
      (string[] splittedString, 
       int index, SuffixNode currentNode)
       {
          Boolean writingFlag = false;
          if (index == 2)
          {
             while(writingFlag == false)
             {
                while(flag == true)
                {
        //blocked
                 }
                 if (flag == false)
                 {
                    flag = true;
                 if(!secondChoiceResults.Contains
                     (currentNode.representingStance.SequenceOfHolds))
                 {
                    Console.WriteLine("new addition");
                    secondChoiceResults.Add
                       (currentNode.representingStance.SequenceOfHolds,
                        currentNode.representingStance);
                  }
                  flag = false;
                  writingFlag = true;
               }
            }

         }
         else
         {
            int nextIndex = index + 1;
            goDownLinkType2
              (splittedString, 
               nextIndex,
               (SuffixNode)currentNode.childHolds[splittedString[nextIndex]]
              );
          }
       }

【问题讨论】:

  • Parallel.ForEach 最终会终止吗?如果是这样,这是设计使然。它正在终止线程池中为满足您的请求而旋转的所有线程。如果它没有终止,是否存在使一个或多个线程保持打开状态的边界条件?
  • 在程序中它应该终止,但在这种情况下它不应该在那个阶段,因为并非所有数据都会被处理。之后它应该到达一个永远不会到达的断点,程序只是挂在 foreach 上。
  • 您是否可能将输出解释为处理的终止,而实际上可能只是 Parallel.ForEach 不再需要这些线程,因此将它们释放到 ThreadPool ?如果您想要一个更具体的答案,您需要告诉我们更多关于您的断点的信息:它在哪里,那里有什么代码,以及为什么您希望它被命中。
  • 如果是这样,可能是 1 个线程卡在 foreach 内的问题?因为所有其他线程都已准备好处理它们并且 1 被卡住了。
  • 将 Parallel.ForEach 与 any 共享数据(如 flagremainingSequence)一起使用通常不是一个好主意。该共享数据由多个线程读取,应该受到保护或通过违规访问来访问...

标签: c# debugging c#-4.0 concurrency task-parallel-library


【解决方案1】:

扔掉'flag'变量并使用lock语句。使用此代码,您的关键部分可能有多个线程(即,一个线程即将设置 flag = true 而另一个线程刚刚将 flag == false 评估为 true(顺便说一句,将来只需使用 !flag)

lock( obj )
{
    // critical section here
}

obj 只需要是对所有线程都可以访问的对象的引用。

这是我对您的代码的修改:

public void goDownLinkType2(string[] splittedString, int index, SuffixNode currentNode)
{
    Boolean writingFlag = false;
    if (index == 2)
    {
        while(writingFlag == false)
        {
            lock( this )
            //while(flag == true)
            //{
                //blocked
            //}
            //if (flag == false)
            {
                //flag = true;
                if (!secondChoiceResults.Contains(currentNode.representingStance.SequenceOfHolds))
                {
                    Console.WriteLine("new addition");
                    secondChoiceResults.Add(currentNode.representingStance.SequenceOfHolds, currentNode.representingStance);
                }
                //flag = false;
                writingFlag = true;
            }
        }


    }
    else
    {
        int nextIndex = index + 1;
        goDownLinkType2(splittedString, nextIndex, (SuffixNode)currentNode.childHolds[splittedString[nextIndex]]);
    }
}

【讨论】:

  • 如果发生这样的事情,即两个线程同时进入,Visual Studio 不会指出它是一个例外吗?
  • 调试并行性问题非常困难,就调试器而言,线程等待不是问题......也许您正在等待一个非常长的异步操作完成。
猜你喜欢
  • 2019-04-26
  • 2014-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-18
  • 1970-01-01
相关资源
最近更新 更多