c# - Parallel.For 在大约 1370 次迭代后卡住,不知道为什么

标签 c# parallel-processing freeze task-parallel-library

我正在对 7500 多个对象运行 Parallel.For 循环。在那个 for 循环中,我对每个对象做了很多事情,特别是调用两个 Web 服务和两个内部方法。 Web 服务只是检查对象、处理并返回一个字符串,然后我将其设置为对象的属性。两个内部方法也是如此。

我没有将任何内容写入磁盘或从磁盘读取。

我还使用标签和进度条更新了 winforms 应用程序中的 UI,让用户知道它的位置。这是代码:

var task = Task.Factory.StartNew(() =>
{
  Parallel.For(0, upperLimit, (i, loopState) =>
  {
     if (cancellationToken.IsCancellationRequested)
        loopState.Stop();
     lblProgressBar.Invoke(
       (Action)
       (() => lblProgressBar.Text = string.Format("Processing record {0} of {1}.", (progressCounter++), upperLimit)));
     progByStep.Invoke(
       (Action)
       (() => progByStep.Value = (progressCounter - 1)));

      CallSvc1(entity[i]);
      Conversion1(entity[i]);
      CallSvc2(entity[i]);
      Conversion2(entity[i]);
  });
}, cancellationToken);

这是在 Win7 32 位机器上进行的。

关于为什么当增量器在 1370 左右(它是 1361、1365 和 1371)时突然卡住的任何想法?

关于如何调试它并查看锁定的内容(如果有的话)有什么想法吗?

编辑:
对以下评论的一些回答:
@BrokenGlass - 不,没有互操作。我会尝试 x86 编译并告诉你。

@chibacity - 因为它在后台任务中,所以它不会卡住 UI。直到它卡住时,进度条和标签以每秒大约 2 的速度滴答作响。当它卡住时,它只是停止移动。我可以验证它停止的数字是否已被处理,但仅此而已。双核 2.2GHz 上的 CPU 使用率在运行期间最低,每次为 3-4%,一旦卡住则为 1-2%。

@Henk Holterman - 大约需要 10-12 分钟才能到达 1360,是的,我可以验证所有这些记录都已处理但未处理其余记录。

@CodeInChaos - 谢谢,我会试试的!如果我取出并行代码,代码确实有效,它只需要永远和一天。我没有尝试限制线程数,但会的。

编辑 2:
关于网络服务的一些细节

Web 服务基本上是在传递一些数据并接收数据(XmlNode)。然后该节点用于 Conversion1 进程,该进程依次设置实体的另一个属性,该属性被发送到 CallSvc2 方法,依此类推。它看起来像这样:

private void CallSvc1(Entity entity)
{
    var svc = new MyWebService();
    var node = svc.CallMethod(entity.SomeProperty);
    entity.FieldToUpdate1.LoadXml(node.InnerXml);
}
private void Conversion1(Entity entity)
{
    // Do some xml inspection/conversion stuff
    if (entity.FieldToUpdate1.SelectSingleNode("SomeNode") == "something") {
        entity.FieldToUpdate2 = SomethingThatWasConverted;
    }
    else {
        // Do some more logic
    }
}
private void CallSvc2(Entity entity)
{
    var svc = new SomeOtherWebService();
    var xmlNode = svc.MethodToCall(entity.FieldToUpdate2.InnerXml);
    entity.AnotherXmlDocument.LoadXml(xmlNode.InnerXml);
}

如您所见,这是非常简单的事情。一些转换方法中发生了很多事情,但没有一个应该是阻塞的。如下所述,有 1024 个线程处于“等待”状态,它们都在进行 Web 服务调用。我在这里阅读http://www.albahari.com/threading/对于 32 位机器上的 .Net 4,MaxThreads 默认为 1023。

我怎样才能释放那些等待中的线程?

最佳答案

一个可能的解释:您已经使进程进入无法创建更多线程的状态,这会阻止工作取得进展,这就是为什么一切都陷入停顿的原因。

坦率地说,无论该假设是否正确,您都需要对此采取完全不同的方法。 Parallel.For 是解决这个问题的错误方法。 (Parallel 最适合 CPU 密集型工作。这里是 IO 密集型工作。)如果您确实需要处理数千个 Web 服务请求,则需要转到使用异步代码,而不是多线程代码。如果您使用异步 API,您将能够在仅使用少量线程的情况下同时启动数千个请求。

这些请求是否真的能够同时执行是另一回事 - 无论您使用当前的“线程启示录”实现还是更高效的异步实现,您都可能会遇到限制。 (.NET 有时会限制它实际发出的请求数。)因此,您可以请求发出任意数量的请求,但您可能会发现几乎所有请求都在等待较早的请求完成。例如。我认为 WebRequest 将与任何单个域的并发连接限制为仅 2 个...启动 1000 多个线程(或 1000 多个异步请求)只会导致加载更多请求等待成为其中之一当前的 2 个请求!

你应该做你自己的节流。您需要决定同时有多少未完成的请求,并确保您一次只启动那么多请求。仅仅要求 Parallel 尽可能快地启动尽可能多的应用程序会使一切陷入困境。

更新添加:

快速修复可能是使用接受 ParallelOptions 对象的 Parallel.For 重载 - 您可以将其 MaxDegreeOfParallelism 属性设置为限制并发请求的数量。这将阻止这个线程繁重的实现实际用完线程。但它仍然是解决问题的低效解决方案。 (据我所知,你确实需要发出数千个并发请求。例如,如果你正在编写网络爬虫,这实际上是一件合理的事情。Parallel 不是不过,该作业的正确类。使用异步操作。如果您使用的 Web 服务代理支持 APM(BeginXxx、EndXxx),您可以将其包装在 Task 对象中 - Task .TaskFactory 提供了一个 FromAsync,它将提供一个表示正在进行的异步操作的任务。

但是如果您打算同时处理数千个请求,则需要仔细考虑您的限制策略。尽可能快地发出请求不太可能是最佳策略。

关于c# - Parallel.For 在大约 1370 次迭代后卡住,不知道为什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4340216/

相关文章:

bash - 如何检查启动后台进程的 bash 脚本的执行时间

java - JVM 在 windows Server 2008 R2 下停在 2GB

iphone - 在 iPhone 模拟器中运行项目后,Xcode 4.6 经常挂起

java - LWJGL grabbed mouse - 如果应用程序挂起或使用抓取的鼠标命中断点时进行调试

c# - 如何将方法从其类中拉出(并放入新的或现有的)?

c# - ASP.NET Core API 仅返回列表的第一个结果

c - MPI 基本示例不起作用

language-agnostic - 并发编程和并行编程有什么区别?

c# - 当另一个完成使用多线程时启动一个方法

c# - 使用 .NET 类的 OpenSSL 加密