我在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 - 到达1360需要大约10-12分钟,是的,我可以验证所有这些记录是否已经处理,但不是剩余的记录。
@CodeInChaos - 谢谢,我会试试的!如果我拿出并行代码,代码确实有用,它只需要一天又一天。我没有尝试过限制线程数,但是会。
编辑2:
关于webservices发生了什么的一些细节
基本上,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位机器上,MaxThreads默认为.Net 4的1023。
我如何根据我的资料释放那些等待的线程?
答案 0 :(得分:9)
一个可能的解释:你已经让这个过程进入了一个无法创建更多线程的状态,这阻碍了工作的进展,这就是为什么一切都停止了。
坦率地说,无论这个假设是否正确,你都需要对此采取完全不同的方法。 Parallel.For
是解决这个问题的错误方法。 (Parallel
最适合CPU绑定工作。这里有你的IO工作。)如果你真的需要有数千个Web服务请求,你需要转向使用异步代码,而不是多线程代码。如果您使用异步API,那么在仅使用少数线程的情况下,您将能够同时启动数千个请求。
这些请求是否真的能够同时执行是另一回事 - 无论您使用当前的“线程启动”实现还是更高效的异步实现,您可能会遇到限制。 (.NET有时可以限制它实际发出的请求数量。)因此,您可以要求尽可能多地发出请求,但您可能会发现几乎所有请求都在等待早期请求完成。例如。我认为WebRequest
将与任何单个域的并发连接限制为仅2 ...将1000多个线程(或1000多个异步请求)连接起来只会导致更多请求等待成为当前的2个当前之一请求!
你应该做自己的节流。您需要确定同时有多少未完成的请求,并确保您一次只启动那么多请求。只要让Parallel
尽可能快地启动它就会使一切都陷入困境。
已更新以添加:
快速修复可能是使用接受Parallel.For
对象的ParallelOptions
重载 - 您可以设置其MaxDegreeOfParallelism
属性以限制并发请求的数量。这将阻止这个线程繁重的实现实际耗尽线程。但它仍然是解决问题的低效办法。 (而且据我所知,你确实需要制作成千上万的并发请求。例如,如果你正在编写一个网络爬虫,那实际上是一件合理的事情。Parallel
不是正确的类尽管如此。使用异步操作。如果您使用的Web服务代理支持APM(BeginXxx,EndXxx),您可以将其包含在Task
个对象中 - Task.TaskFactory
提供{{1这将提供表示正在进行的异步操作的任务。
但是,如果您要尝试同时处理数千个请求,您需要仔细考虑您的限制策略。只是尽快将请求丢弃在那里不太可能是最佳策略。
答案 1 :(得分:5)
在VS调试器中运行应用程序。当它似乎锁定时,告诉VS调试:全部中断。然后转到Debug:Windows:Threads并查看进程中的线程。其中一些应该显示并行for循环中的堆栈跟踪,这将告诉您在调试器停止进程时它们正在做什么。