我没有写任何东西到磁盘或从磁盘读取.
我还在一个带有标签和进度条的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 32bit机器上进行的.
任何想法,为什么当增量大约在1370左右时突然冻结(这是1361年,1365年和1371年)?
任何想法,我如何调试这个,看看什么是锁定,如果有什么?
编辑:
以下评论的一些答案:
@BrokenGlass – 不,没有互操作.我会尝试x86编译,让你知道.
@chibacity – 因为它是在后台任务,它不冻结UI.直到它冻结的时候,进度条和标签每秒约2秒.当它冻结,它只是停止移动.我可以验证它停止的号码已经被处理,但没有更多.双核2.2GHz的cpu使用率在运行期间为3-4%,一旦冻结,则为1-2%.
@Henk Holterman – 需要大约10-12分钟才能到达1360,是的,我可以验证所有这些记录是否已被处理,但没有剩余的记录.
@CodeInChaos – 谢谢,我会试试!如果我取出并行代码,这个代码就可以工作,它只需要永远的一天.我没有尝试限制线程数,但会的.
编辑2:
有关Web服务发生了什么的一些细节
基本上,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是错误的解决方法. (并行最适合于cpu绑定的工作,这里是IO绑定的工作.)如果您真的需要正在进行数千个Web服务请求,则需要转移到使用异步代码,而不是多线程代码.如果您使用异步API,您将能够同时启动数千个请求,同时仅使用少量线程.
无论这些请求是否能够同时执行,这是另一回事 – 无论您使用当前的“线程启示”实现还是使用更高效的异步实现,您都可能会遇到挫败. (.NET有时可以限制实际需求的数量).所以你可以要求尽可能多的请求,但是你可能会发现几乎所有的请求都是等待早期的请求来完成的.例如.我认为WebRequest限制与任何单个域的并发连接只有2 …启动1000个线程(或1000个异步请求)将导致加载更多的请求,等待是2个当前请求之一!
你应该做自己的节流.您需要确定同时有多少未完成的请求,并确保您只需一次启动该请求.只要求Parallel启动尽可能快的就可以把所有的东西全部掉下来.
更新添加:
快速修复可能是使用Parallel.For接受ParallelOptions对象的重载 – 您可以设置其MaxDegreeOfParallelism属性来限制并发请求数.这将阻止这个线程繁重的实现实际上用完线程.但是这个问题仍然是一个低效的解决方案. (所有我知道的是,你确实需要做出数千个并发请求,例如,如果你正在编写一个网页抓取工具,那实际上是一件合理的事情,并不是这个工作的正确的类,使用异步操作如果Web服务代理使用支持APM(BeginXxx,EndXxx),则可以将其包装到任务对象中 – Task.TaskFactory提供了一个FromAsync,它将提供一个表示正在进行的异步操作的任务.
但是,如果你要试图在一次飞行中有数千个请求,你需要仔细考虑你的扼制策略.只要尽可能快地将请求尽可能地放在最佳策略上.